Estatística Computacional

capa

1. O que é Estatística Computacional?

Em geral, a análise estatística envolve conhecimentos sobre um fenômeno para desenvolver um modelo que possibilite o estudo e o entendimento deste fenômeno ou processo que geram os dados. Após a definição do modelo, os dados observados são utilizados para refinar o modelo escolhido ou para selecionar um modelo diferente, ou, ainda, para determinar valores para termos desconhecidos do modelo e para fazer inferência acerca do processo de geração dos dados. Nos dias atuais, este paradigma foi diretamente afetado pelos avanços computacionais, que não somente viabilizaram o desenvolvimento e a aplicação de métodos muito mais sofisticados e complexos, do que aqueles usados antes desta revolução, mas também permitiram o exame de muitas outras visões dos dados.

A partir desses avanços, uma nova visão da Estatística começou a ser estudada, chamada de Estatística Computacional. A Estatística Computacional diz respeito ao desenvolvimento e uso de algoritmos de computador para fornecer soluções numéricas para problemas em estatística que são analiticamente difíceis ou intratáveis. NEsse sentido, alguns problemas para os quais a estatística computacional é aplicada incluem análise exploratória de dados, inferência estatística, otimização, métodos de reamostragem, integração numérica, e simulação de variáveis ou processos aleatórios. Neste curso, iremos abordar todos esses conceitos por meio do Ambiente e Linguagem de Programação R.

2. Ambiente R

Fazendo parte da filosofia do Projeto GNU e estando disponível como Software Livre sob os termos da “Licença Pública Geral do GNU” da Fundação do Software Livre (Free Software Foundation’s GNU General Public License) na forma de código fonte, o R é ao mesmo tempo uma linguagem de programação e um ambiente para computação estatística e gráfica, isto é, trata-se de uma linguagem de programação especializada em computação com dados. Além de gratuito, o R está disponível para uma gama bastante variada de sistemas operacionais como, por exemplo, UNIX, FreeBSB, Linux, Windows e MacOS.

R

O R foi criado originalmente por Ross Ihaka e por Robert Gentleman na Universidade de Auckland, Nova Zelândia, e foi desenvolvido por um esforço colaborativo de pessoas em vários locais do mundo. O nome R provém em parte das iniciais dos criadores e também de um jogo figurado com a linguagem S (da Bell Laboratories, antiga AT&T). Resumidamente, então, pode-se dizer que o R é um conjunto integrado de instalações de software para manipulação de dados, cálculos e exibição gráfica que tem como característiscas:

  • Instalação de manipulação eficaz e armazenamento de dados,
  • Conjunto de operadores para cálculos em matrizes, em particular matrizes,
  • Coleta ampla, coerente e integrada de ferramentas intermediárias para análise de dados,
  • Instalações gráficas para análise de dados e exibição em tela ou em cópia impressa, e
  • Linguagem de programação bem desenvolvida, simples e eficaz, que inclui condicionais, laço de repetiçãos, funções recursivas definidas pelo usuário e instalações de entrada e saída.

2.1 Instalando o R

As etapas a seguir podem ser usadas para instalar o R em um sistema operacional Windows. Para instalar o R em um sistema operacional Macintosh ou Linux, consulte o website: https://cran.rstudio.com.

  • 1º Passo: Acesse RStudio Comprehensive R Archive Network (CRAN) em https://cran.rstudio.com. [Você também pode selecionar um domínio diferente acessando http://www.r-project.org, selecionando o link “download R” na caixa “Getting Started” e selecionando um domínio na página seguinte.]

  • 2º Passo: Na página aberta, selecione o link “Baixar R para Windows”.

R
  • 3º Passo: Selecione a opção “base”.
R


  • 4º Passo: Selecione a opção “Download R 4.2.2 for Windows”. Em seguida, execute o programa ou anote onde você salvou esse programa executável em seu computador.
R


  • 5º Passo: Selecione o idioma “Inglês” na primeira caixa de diálogo (dependendo da sua versão do Windows, você pode ter recebido avisos de segurança antes desta caixa de diálogo aparecer). Pressione Next nas próximas duas caixas de diálogo (primeiro, uma descrição simples; segundo, um contrato do usuário).

R


  • 6º Passo: Neste ponto, você pode optar por instalar 32 ou 64 bits ou ambas as versões do R. Se você não tiver um computador de 64 bits, deverá instalar a versão de 32 bits. Se você tiver um computador de 64 bits, inicialmente e para simplificar, instale apenas uma versão ou outra. Em seguinda, pressione Next.

R


  • 7º Passo: Selecione um local para instalar o R (basta usar o local padrão se o local não for importante para você), e pressione Avançar. Em seguida, selecione a opção “Não (aceitar padrões)” (este é o padrão). Decida se deseja criar um atalho na pasta do Menu Iniciar e se deseja ou não criar ícones de área de trabalho ou Quick Launch (duas opções principais) e, também, se deseja registrar o número da versão e associar arquivos .RData com R (duas opções inferiores), e pressione Next.

R


  • 8º Passo: R deve então começar a instalar arquivos no diretório que você escolheu anteriormente. Se tudo correr bem, você deve obter uma última caixa de diálogo observando isso. Então, basta pressionar Done.

2.2. Interfaces para o R: O RStudio

Ao contrário de muitos outros programas, os usuários interagem com o R por meio de uma linha de comando, e não por meio de uma interface gráfica do usuário. Embora essa interface possa não ser familiar para muitos usuários, sua força principal é a capacidade dos usuários de desenvolver scripts de comandos para realizar várias análises que podem ser facilmente repetidas. Todavia, nos dias atuais, pode-se trabalhar com ambientes de desenvolvimento integrado (IDE) para deixar o uso do R mais amigável. Dentre todas as IDEs disponíveis, a mais popular é o RStudio.

O RStudio é um ambiente de desenvolvimento integrado (IDE) de código-fonte aberto que serve como um front-end “no topo” do R. O RStudio facilita a interação do usuário com o R fornecendo algumas das conveniências de uma GUI e, mais importante, um meio para construindo e executando scripts R de forma eficiente. Entre outras conveniências, o RStudio fornece um layout de quatro painéis que inclui um editor de código-fonte rico em recursos (inclui realce de sintaxe, conclusão de parênteses, verificação ortográfica etc.), um link estreito para o console R, um sistema para examinar objetos salvo em R, uma interface para a ajuda do R e recursos estendidos para examinar e salvar gráficos.

RStudio


Para instalar a IDE do RStudio, basta seguir os passos:

  • 1º Passo: Acesse a página de download do RStudio disponível em: https://posit.co/download/rstudio-desktop/#download.

  • 2º Passo: Selecione o link na lista “Instaladores para plataformas suportadas” que corresponde ao sistema operacional apropriado para o seu computador. Execute o programa baixado e siga as instruções da tela.

2.3. Conceitos Básicos do R

2.3.1. Tipos de Estruturas de Dados

Basicamente, há quatro tipos de dados no R: númericos, caracteres, lógicos e números complexos. Cada objeto possui dois atributos: tipo (mode) e o tamanho (length). Além desses atributos, os tais podem ser classificados em certas estruturas conhecidas como vetores, data.frames, array, matrizes e listas. É importante destacar que é preciso ter claro como são dispostas as estruturas de dados no R.

  • Vetores (c) - Conjuntos de dados unidimensionais do mesmo tipo.
# Exemplos:

x <- c(1, 4, 10.5, 54.48, 9, 10)
x
## [1]  1.00  4.00 10.50 54.48  9.00 10.00
y <- seq(from = 1, to = 10, by = 0.5)
y
##  [1]  1.0  1.5  2.0  2.5  3.0  3.5  4.0  4.5  5.0  5.5  6.0  6.5  7.0  7.5  8.0
## [16]  8.5  9.0  9.5 10.0
z <- c('Vetor', 'Data.Frame', 'Array', 'Matrizes', 'Listas')
z
## [1] "Vetor"      "Data.Frame" "Array"      "Matrizes"   "Listas"
  • Matrizes (matrix) - Conjuntos bidimensionais de dados do mesmo tipo. As matrizes não são consideradas agrupamento de vetores.
# Exemplos:

A <- matrix(data = c(1:10), nrow = 2, ncol = 5, byrow = TRUE)
A
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    2    3    4    5
## [2,]    6    7    8    9   10
B <- matrix(data = seq(from = 1, to = 10, by = 1), nrow = 2, ncol = 5, byrow = TRUE)
B
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    2    3    4    5
## [2,]    6    7    8    9   10
  • Arrays (array) - Conjunto de dados do mesmo tamanho e tipo de dados. Nos arrays, os elementos individuais são acessados por sua posição que é dada por um índice, chamado de subscrição.
# Exemplo:

ar <- array(c(1:10), dim = c(2,5))
ar
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    3    5    7    9
## [2,]    2    4    6    8   10
  • Data.frames (data.frame) - Conjuntos bidimensionais de vetores de mesmo comprimento, sendo que cada vetor pode ser de um tipo diferente. Nos data.frames os vetores são agrupados pelas colunas.
# Exemplo:

A  <- matrix(data = c(1:10), nrow = 2, ncol = 5, byrow = TRUE)
db <- data.frame(A)
db
##   X1 X2 X3 X4 X5
## 1  1  2  3  4  5
## 2  6  7  8  9 10
  • Listas (list) - Conjuntos de dados de qualquer tipo, incluindo listas de listas. Cada elemento da lista pode ser considerado um “vetor”.
# Exemplo:

x     <- c(1, 4, 10.5, 54.48, 9, 10)
A     <- matrix(data = c(1:10), nrow = 2, ncol = 5, byrow = TRUE)
db    <- data.frame(A)
lista <- list(x, A, db)
lista
## [[1]]
## [1]  1.00  4.00 10.50 54.48  9.00 10.00
## 
## [[2]]
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    2    3    4    5
## [2,]    6    7    8    9   10
## 
## [[3]]
##   X1 X2 X3 X4 X5
## 1  1  2  3  4  5
## 2  6  7  8  9 10

2.3.2. Comandos do Workspace

Abaixo veremos uma tabela com os principais comandos que ajudam a manipular os objetos e a workspace que estão sendo utilizados durante a execução do programa R.

Função Descrição
ls() ou objects() Lista curta de variáveis definidas
ls.str() Lista detalhada de variáveis definidas
str(x) Ver informações detalhadas de x
ls.str(ab) Ver informações detalhadas sobre todas as variáveis com “ab” em seu nome
rm(x) Deletar variável x
rm(x, y) Deletar as variáveis x e y
rm(list = ls()) Deletar todas as variáveis (limpar a workspace)
class(x) Ver que tipo de objeto é x
q() Sair do R com a opção de salvar a workspace em um arquivo (“Name.RData”)
setwd() Define o diretório de dados
getwd() Resgata o diretório de dados
download.file() Realiza download de dados provenientes da internet
$ Operador para selecionar uma variável da base de dados
[] Operador para selecionar uma posição dos dados
ctrl + L No teclado, pressione “ctrl+L” para limpar a tela da console

2.3.3. Instalando Pacotes

Um pacote do R é uma coleção de funções, dados e documentação que estende as funcionalidades básicas do R, sendo desenvolvidos pela comunidade do R que é formada por diversos contribuidores. Por default, na instalação do R são instalados apenas os pacotes básicos e recomendados para o funcionamento do R. No entanto, é possível instalar pacotes de outras fontes como, por exemplo, GitHub e R-Forge.

Em geral, a forma mais fácil de instalar uma pacote do R é através da função install.packages("nome_do_pacote"). Por este comando, o pacote informado é instalado a partir do repositório oficial de distribuição de pacotes: a (CRAN). A CRAN é uma rede de servidores e FTP distribuídas pelo mundo e mantida pela comunidade R. É importante destacar que a Fundação R coordena a CRAN e estabelece diversos testes para assegurar que os pacotes publicados sigam as políticas da CRAN. Atualmente, existem mais de 2.000 pacotes disponíveis para download gratuito do CRAN que passaram por tais políticas.

Para exemplificar a instalação de pacotes do CRAN, instalaremos o pacote remotes que dispõe de funções para instalar pacotes de repositórios remotos, como por exemplo do GitHub.

# install.packages("remotes")

# Para ter acesso as funções disponibilizadas com o pacote você precisa carregar o pacote:

# library(remotes)

# Apesar de precisar só instalar uma vez um pacote, você precisará carregá-lo a cada nova sessão.

Para desinstalar um pacote você pode usar a função remove.packages("nome_do_pacote").

2.3.3.1. Pacotes do GitHub e R-forge

Nem todos pacotes são disponíveis na CRAN. Muitos desenvolvedores disponibilizam seus pacotes em plataformas como o GitHub e R-forge. Às vezes um pacote pode estar em ambos CRAN e GitHub (ou R-forge), mas a última versão - a de desenvolvimento - é somente disponibilizada no GitHub (ou R-forge).

Para instalar um pacote de um repositório do GitHub usa-se a função install_github() do pacote remotes. Portanto, o pacote remotes precisa ser sido instalado primeiro para trabalhar com o GitHub. Além disso, a função para instalar um pacote do GitHub requer como argumento o nome do usuário/nome do repositório. Por exemplo, para instalar o pacote ADARdata do repositório mantido pelo lhmet, utiliza-se os comandos:

# library(remotes)
# install_github("lhmet/ADARdata")

Você pode acessar uma função de um pacote instalado com a forma especial `pacote::funcao``. Nesse sentido, o trecho de código anterior poderia ser reduzido a:

# remotes::install_github("lhmet/ADARdata")

Essa forma deixa explícito que estamos usando a função install_github() do pacote remotes.

Por outro lado, para instalar um pacote em um repositório do R-forge, por exemplo, o repositório do pacote raster, utiliza-se os comandos:

# install.packages("raster",repos = "http://R-Forge.R-project.org")

2.3.3.2. Arquivo Local

Uma outra alternativa de instalação de pacotes, é a utilização de arquivos de pacotes locais. Em geral, os códigos fonte de pacotes do R são armazenados como arquivos com a extensão .tar.gz. Binários compilados são armazenados com a extensão .zip. Exemplo de arquivos como estes podem ser baixados manualmente da CRAN (veja a seção Downloads em por exemplo, https://cran.r-project.org/web/packages/remotes/index.html), no GitHub ou R-forge.

Assim, para instalar um pacote a partir desses arquivos localmente, utiliza-se a função install.packages(), especificando o argumento repos = NULL e o argumento pkgs com o caminho do arquivo. Por exemplo:

# install.packages(pkgs = "remotes_2.1.1.tar.gz", repos = NULL)

2.3.4. O Comando help

Durante a utilização do software é possível consultar a sintaxe de algum comando ou obter mais informações sobre determinada função. Para isso o R conta com o comando help(). A sintaxe do comando é a seguinte:

# help(comando) 

# Exemplo:
# help(sqrt)

Ao executar o exemplo acima, uma interface do menu de ajuda será executada mostrando o tópico da função sqrt, que é função matemática para a raiz quadrada. Para realizar uma busca em arquivos de ajuda sobre um tópico desejado, podemos utilizar os seguintes comandos:

# help.search("expressão") 
# ??expressão 

# Exemplos:
# help.search("negative binomial")
# ??weibull

Em relação aos pacotes, cada pacote possui uma página de resumo com uma descrição curta e links para a documentação do pacote. Depois de localizar um pacote potencialmente interessante, você pode clicar no link “Manual de referência” para visualizar a documentação em PDF com todos os detalhes, ou utilizar o comando:

# help(package="packagename")

# Exemplo:
# help(package="tseries")

2.3.5. Atribuição de Valores

Como todo tipo de programação (inclusive funcional), é comum que tenhamos que atribuir valores para algumas variáveis antes de utilizá-las (esse processo também é conhecido como inicialização de variáveis). No R podemos fazer uma atribuição de valores de várias formas, conforme os exemplos abaixo:

x <- 10 # x é a variável que recebe o valor 10;

0.56 -> x # x é a variável que recebe o valor 0.56;

x = -8 # x é a variável que recebe o valor -8;

assign("x", 2i) # x é a variável que recebe o imaginário 2i;

Na maior parte do tempo utilizaremos os símbolos “<−” e “=” para atribuição de valores, isto é, variavel <- valor. Em geral, uma atribuição armazena o valor (no lado direita da atribuição) em uma variável (no lado esquerdo da atribuição, sendo que a variável é um nome usado para guardar os dados.

Para visualizar o valor de uma variável, basta digitar o nome da variável na linha de comando, ou imprimir seu valor com a função print(). Isto é,

x <- 10 # x é a variável que recebe o valor 10;

x
## [1] 10
print(x)
## [1] 10

2.3.6. Operações Matemáticas

Os símbolos da linguagem R para realizar as operações matemáticas básicas são construídos através dos operadores usuais e das regras de precedência de qualquer linguagem de edição de dados, isto é,

1 ^ Potenciação
2 / Divisão
3 * Multiplicação
4 + Adição
5 - Subtração

Além dos operadores matemáticos, o R também possui dois tipos de operadores importantes: os operadores relacionais e operadores lógicos. Os operadores relacionais, permitem estabelecer a relação entre dois valores de entrada, e retornar um valor lógico verdadeiro ou falso dependendo da relação. Por exemplo, um operador de comparação pode comparar dois números e dizer se eles são iguais ou não. Tais operadores são:

Símbolo Descrição
\(<\) Menor
\(<=\) Menor ou igual
\(>\) Maior
\(>=\) Maior ou igual
\(==\) Igual (comparação)
\(!=\) Diferente

É importanto destacar que os operadores de comparação sempre retornam um valor lógico TRUE ou FALSE. Por outro lado, os operadores lógicos, também conhecidos como operadores booleanos, permitem trabalhar com múltiplas condições relacionais na mesma expressão, e retornam valores lógicos verdadeiro ou falso. Tais operadores são:

Símbolo Descrição
\(\&\) AND (versão vetorizada)
\(\&\&\) AND (versão não-vetorizada)
\(|\) OR (versão vetorizada)
\(||\) OR (versão não-vetorizada)
\(!\) NOT (negação lógica)
xor XOR (Ou exclusivo)
TRUE Valor booleano verdadeiro
FALSE Valor booleano falso

2.3.7. Criação de Funções

Em geral, as funções são atribuídas e salvas em objetos, sendo declaradas com a palavra function. Os argumentos, obrigatórios ou opcionais, são definidos na declaração da função entre parênteses. Devido ao fato que normalemnte as funções possuem mais de uma linha de código, os blocos de código da função são especificados entre chaves e a função return() especifica o que é retornado. Em termos de código, uma função é escrita como:

nome            <- function(argumento_1, ..., argumento_n) 
{
    # Corpo / Comandos da função
  
    # return()
}

Em outras palavras, toda função tem como elementos básicos:

  • Nome: Precisamos dar um nome à nossa função, e já devemos ter atenção neste passo. O nome da função deve seguir às mesmas regras de nomeação de variáveis, como, por exemplo, não começar com números. Ao nome de nossa função atribuímos o comando function(). Depois de pronta, a função será executada usando o nome que atribuímos.

  • Argumentos da função: Devemos pensar em quais informações nossa função irá precisar, que chamamos de argumentos da função. Se queremos uma função que calcule a média de um vetor, por exemplo, precisamos do vetor ou do somatório dos elementos do vetor e do comprimento do mesmo. Esses argumentos são colocados dentro do comando function() separados por vírgula.

  • Corpo/Comandos da função: Agora entra o código que faz o que a função necessita. Dependendo do objetivo da função teremos que usar estruturas de controle, como condições ou laço de repetiçãos, ou poderemos usar recursão. Basicamente todos os comandos que já conhecemos podem estar nesta parte. O importante é que todos esses comandos que fazem parte da função devem estar delimitados. Precisamos especificar onde começa e onde acaba a nossa função, assim como fazemos com laço de repetiçãos e com o if(), por exemplo.

  • return(): Um comando não obrigatório, mas que é bastante comum no final das funções é o return(). Nossa função vai obter algum resultado e este comando faz com que o R retorne o objeto dentro dos parênteses de return() no console.

Por outro lado, as funções também podem ser definidas minimamente. Essa definição mínima é viável apenas para funções com apenas uma linha de código, neste caso, não são usadas nem as chaves ({}) para especificar o bloco de códigos nem a função return, já que apenas o objeto resultante da única linhas de código é retornado. Por exemplo, podemos trabalhar com as seguintes estruturas de código:

# Atribuição de uma típica função

funcao.tipica   <- function(x)
{
    result <- x+1
    return(result) 
}

# Função mímina, apenas com os elementos essenciais

funcao.minima   <- function(x) x+1

A primeira coisa que deve ser observada é que funções não se limitam a funções matemáticas: uma função recebe alguns inputs, faz algumas operações e devolve um output. Vale lembrar que as funções são criadas para tarefas que serão repetidas várias vezes, sendo assim, não faz nenhum sentido escrever uma função que só será usada uma única vez. Em geral, escrever a função exige, de antemão, que você saiba o tamanho dos vetores, matrizes, como o código irá se comportar, etc.

2.3.8. Laços de Repetição

“Looping”, “cycling”, “iterating” ou apenas replicação de instruções é uma prática antiga que se originou bem antes da invenção dos computadores. Nada mais é do que automatizar um processo de várias etapas, organizando sequências de ações ou processos “em lote” e agrupando as partes que precisam ser repetidas. Todas as linguagens modernas de programação fornecem construções especiais que permitem a repetição de instruções ou blocos de instruções.

De um modo geral, existem dois tipos dessas construções ou laços de repetição especiais nas linguagens de programação modernas. Alguns laços são executados por um determinado número de vezes, conforme controlado por um contador ou índice, incrementado a cada ciclo de iteração. Estes fazem parte da família de laços de repetição for. Por outro lado, alguns laços baseiam-se no início e na verificação de uma condição lógica. A condição é testada no início ou no fim da construção do laço. Estas variantes pertencem à família while ou repeat, respectivamente.

2.3.8.1. Laços de Repetição for

A estrutura de repetição for permite criar loops para casos onde sabemos de antemão o número de repetições que devem ser realizadas. Para exemplificar, vamos supor que devemos imprimir os números de 1 até 10. Para resolver esses problema, podemos executar o comando print() dez vezes, no entanto, isso se tornaria inviável se em vez de 1 até 10, estivéssemos interessados em imprimir os números de 1 até 100. Então, como alternativa a esse processo, podemos trabalar com o laço de repetição for, isto é,

# Estrutura do laço for:

# for (variável in vetor_números) 
# {
#   Corpo do laço
# }

for (i in 1:10) 
{
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

2.3.8.2. Laços de Repetição while

A estrutura while realiza um teste lógico no início do laço, e cada vez que esse teste retorna o valor verdadeiro, os comandos associados ao laço são executados. Quando o teste retornar o valor falso, o laço é interrompido. Para exemplificar, vamos considerar a seguinte rotina:

# Estrutura do laço while:

# while(teste)  
# {
#   Corpo do laço
# }

x <- 1
while(x <= 10) 
{
  print(x)
  x <- x + 1
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

2.3.8.3. Laços de Repetição repeat

A estrutura repeat faz com que o R repita o código a seguir sem condições. Com isso, precisamos de mais uma função para mostrar ao programa quando deve parar de repetir o código, chamada de break(). Como sempre existe uma condição a ser satisfeita para o código continuar a ser repetido ou parar, então devemos também usar a função if(). Para exemplificar, vamos considerar a seguinte rotina:

# Estrutura do laço repeat:

# repeat 
# {
#  Comandos a serem repetidos

#  if (Condição para que a repetição pare) break()
# }

j <- 1
repeat
{
  print(j)
  j <- j+1
  if (j > 10) break()
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

2.3.9. Família apply

As funções da família apply são alternativas aos laço de repetição/laços, elas pertencem ao pacote base do R e são projetadas para serem aplicadas a diferentes estruturas de dados como vetores, data.frames, matrizes e listas. As funções permitem cruzar os dados de várias maneiras e evitar, assim, o uso explícito de laço de repetição/laços. Essas funções funcionam de maneira similar, todas requerem uma entradas de dados organizada em uma das estruturas básicas e aplicam uma segunda função a estrutura, sendo possível passar argumentos opcionais para a função aplicada. Os resultados da aplicação podem ser simples, como estátisticas descritivas e transformações ou mesmo retornar estruturas complexas como matrizes ou listas.

De uma maneira ampla, as funções desta família ajudam a executar operações com poucas linhas de código, e é composta principalmente por apply, sapply, lapply, tapply e mapply, e funções associadas que compartilham da mesma lógica como sweep, replicate, entre outras. Para exemplificar o uso da família apply, considere um banco de dados relativo as medidas das pétalas e sépalas de certas espécies de plantas disponível no software R. Este banco de dados será referido como iris.

# Carregar os dados

data(iris)

# Visualização dos dados

head(iris, n = 10)
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1           5.1         3.5          1.4         0.2  setosa
## 2           4.9         3.0          1.4         0.2  setosa
## 3           4.7         3.2          1.3         0.2  setosa
## 4           4.6         3.1          1.5         0.2  setosa
## 5           5.0         3.6          1.4         0.2  setosa
## 6           5.4         3.9          1.7         0.4  setosa
## 7           4.6         3.4          1.4         0.3  setosa
## 8           5.0         3.4          1.5         0.2  setosa
## 9           4.4         2.9          1.4         0.2  setosa
## 10          4.9         3.1          1.5         0.1  setosa

2.3.9.1. Função apply

A função apply aplica funções nas margens de matrizes. É preciso definir qual das margem da matriz a função será aplicada: se MARGIN = 1 a função é aplicada nas linhas, se MARGIN = 2será aplicada nas colunas, ainda, se MARGIN = c(1,2) a função é aplicada das linhas e colunas. Além disso, é preciso difinir qual função será aplicada usando o argumento FUN e quaisquer outros argumentos da função a ser aplicada devem ser passados pelo argumento. Para exemplificar, vamos trabalhar com a soma de todos os valores das colunas da nossa base de dados iris, isto é,

# Uso da função apply():

# apply(X, MARGIN, FUN, ...)

apply(X = iris[,1:4], MARGIN = 2, FUN = sum)
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##        876.5        458.6        563.7        179.9

2.3.9.2. Função tapply

A função tapply divide as estruturas de dados e aplica funções a cada subconjunto, geralmente as funções passada calculam estatísticas descritivas. O argumento INDEX especifica um ou mais fatores para dividir os elementos da estrutura. O funcionamento dessa função é similar as funções aggregate e by. Para ilustrar melhor esta função, vamos considerar agora a variável Species do nosso banco de dados iris. Suponha que nosso objetivo seja calcular a soma de todos os valores dos tamanhos de sépala (variável Sepal.Length) de acordo com a espécie de planta (variável Species). Neste caso, então, temos:

# Uso da função tapply():

# tapply(X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE)

tapply(iris$Sepal.Length, INDEX = iris$Species, FUN = sum)
##     setosa versicolor  virginica 
##      250.3      296.8      329.4

2.3.9.3. Função sapply

A função sapply aplica funções a cada elemento de um vetor, pode ser aplicada em vetores unidimensionais, data.frames (agrupamentos de vetores) e listas (também considerados vetores). Por padrão a função simplifica os resultados se possível, então pode retornar tanto único vetor, uma matriz ou listas. O argumento simplify determina o comportamento da simplificação, sendo que quando simplify = FALSE sempre é retornado uma lista. Para ilustrar melhor esta função, suponha que nosso objetivo seja calcular a soma de todos os valores das variáveis numéricas da nossa base de dados iris. Neste caso, então, temos:

# Uso da função sapply():

# sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)

sapply(iris[,1:4], FUN = sum, simplify = FALSE)
## $Sepal.Length
## [1] 876.5
## 
## $Sepal.Width
## [1] 458.6
## 
## $Petal.Length
## [1] 563.7
## 
## $Petal.Width
## [1] 179.9

2.3.9.4. Função lapply

A função lapply funciona praticamente igual à sapply e permite a aplicação de uma função em cada elemento de um vetor unidimensional, data.frame ou lista. A principal diferença entre as elas é que a função lapply sempre retorna uma lista com os resultados. É preferível manter a função sapply na aplicação de vetores e lapply para aplicação de listas. Para ilustrar melhor esta função, suponha, novamente, que nosso objetivo seja calcular a soma de todos os valores das variáveis numéricas da nossa base de dados iris. Neste caso, então, temos:

# Uso da função lapply():

# lapply(X, FUN, ...)

lapply(iris[,1:4], FUN = sum)
## $Sepal.Length
## [1] 876.5
## 
## $Sepal.Width
## [1] 458.6
## 
## $Petal.Length
## [1] 563.7
## 
## $Petal.Width
## [1] 179.9

2.4. Importação de Dados

Dados para análise estatística podem estar em diversos formatos (tipos de arquivos). O usuário poderia, por exemplo, ter criado um arquivo num software de planilha eletrônica, ou no próprio pacote estatístico que será utilizado. Alguns dos formatos mais usados são mostrados a seguir:

  • Planilha eletrônica (.xls, .xlsx, .ods)
  • Valores separados por vírgulas (.csv)
  • Dbase (.dbf)
  • Diversos Pacotes estatísticos: (.Rdata - R), (.sav - SPSS), etc

No geral, as funções disponíveis no R para importação de dados são:

  • read.csv(): para leitura de arquivos “valores separados por vírgula” (“.csv”).
  • read.csv2(): variante utilizada em países que utilizam vírgula “,” como ponto decimal e ponto e vírgula “;” como separadores de campo.
  • read.delim(): para leitura de arquivos “tab-separated value” (“.txt”). Por padrão, o ponto (“.”) é usado como pontos decimais.
  • read.delim2(): para leitura de arquivos “tab-separated value” (“.txt”). Por padrão, a vírgula (“,”) é usada como pontos decimais.

A sintaxe simplificada dessas funções são definidas comos:

  • Leitura de arquivos delimitados por tabulações, forma geral:

    • read.table(file, header = FALSE, set = ““, dec =”.”)

  • Leitura de arquivos de “valores separados por vírgula” (“.csv”):

    • read.csv(file, header = TRUE, set = “,”, dec = “.”, …)
    • read.csv2(file, header = TRUE, set = “;”, dec = “,”, …)

  • Leitura arquivos delimitados por tabulações, forma específica:

    • read.delim(file, header = TRUE, set = “, dec =”.”, …)
    • read.delim2(file, header = TRUE, set = “, dec =”,“, …)

Em todas elas, os argumentos são:

  • file: o caminho para o arquivo que contém os dados a serem importados para o R.
  • sep: o caractere separador de campo. “ é usado para arquivo delimitado por tabulações.
  • header: valor lógico. Se TRUE, read.table() assume que seu arquivo tem uma linha de cabeçalho, então a linha 1 é o nome de cada coluna. Se não for o caso, você pode definir esse argumento como FALSE.
  • dec: o caractere usado no arquivo para pontos decimais.

Para exemplificar, nesta seção, vamos mostrar como importar para o R de um arquivo de dados do .csv por meio da função read.csv(). Neste caso, podemos seguir a seguinte rotina:

# Definir o diretório onde se encontram os dados

setwd('datasets')

# Leitura dos dados pela função read.csv():

# read.csv(file, header = TRUE, sep = ",", quote = "\"",
#         dec = ".", fill = TRUE, comment.char = "", ...)

db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(db.heart, n = 10)
##    Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1    97     0      0       0        0  0       1  43                50    135
## 2   180     0      1       1        1  0       1  73                30    142
## 3    31     1      1       1        0  1       0  70                20    134
## 4    87     0      1       0        0  0       1  65                25    141
## 5   113     0      1       0        0  0       0  64                60    137
## 6    10     1      1       0        0  0       1  75                15    137
## 7   250     0      1       1        0  0       0  70                40    136
## 8    27     1      1       0        1  1       0  94                38    134
## 9    87     0      1       0        0  1       0  75                45    137
## 10   87     0      1       1        0  0       0  80                25    144
##    Creatinine Pletelets  CPK
## 1        1.30    237000  358
## 2        1.18    160000  231
## 3        1.83    263358  582
## 4        1.10    298000  305
## 5        1.00    242000 1610
## 6        1.20    127000  246
## 7        2.70     51000  582
## 8        1.83    263358  582
## 9        1.18    263358  582
## 10       1.10    149000  898
# Estrutura dos dados

str(db.heart)
## 'data.frame':    299 obs. of  13 variables:
##  $ Time             : int  97 180 31 87 113 10 250 27 87 87 ...
##  $ Event            : int  0 0 1 0 0 1 0 1 0 0 ...
##  $ Gender           : int  0 1 1 1 1 1 1 1 1 1 ...
##  $ Smoking          : int  0 1 1 0 0 0 1 0 0 1 ...
##  $ Diabetes         : int  0 1 0 0 0 0 0 1 0 0 ...
##  $ BP               : int  0 0 1 0 0 0 0 1 1 0 ...
##  $ Anaemia          : int  1 1 0 1 0 1 0 0 0 0 ...
##  $ Age              : num  43 73 70 65 64 75 70 94 75 80 ...
##  $ Ejection.Fraction: int  50 30 20 25 60 15 40 38 45 25 ...
##  $ Sodium           : int  135 142 134 141 137 137 136 134 137 144 ...
##  $ Creatinine       : num  1.3 1.18 1.83 1.1 1 1.2 2.7 1.83 1.18 1.1 ...
##  $ Pletelets        : num  237000 160000 263358 298000 242000 ...
##  $ CPK              : int  358 231 582 305 1610 246 582 582 582 898 ...

O código R acima assume que o arquivo “heart.csv” está em seu diretório de trabalho atual. No entanto, caso o diretório onde se encontra os dados não seja seja derfinido, é possível fazer a importação dos dados escolhendo um arquivo de forma interativa usando a função file.choose(). Neste caso, partimos da seguinte rotina no R:

## Leitura de um arquivo .txt

# meus_dados <- read.delim(file.choose())

## Leitura de um arquivo .csv

# meus_dados <- read.csv(file.choose())

## Em ambos os casos, você será solicitado a escolher um arquivo.

2.4.1. Planilhas do Excel

Em relação a planilhas do Excel, existem alguns pacotes disponíveis que podem ler dados diretamente de planilhas do MS Excel. No entanto, estes pacotes geralmente possuem particularidades quanto ao sistema operacional e demais dependências para funcionar corretamente. Um destes pacotes, é o readxl, desenvolvido por Hadley Wickman, que funciona em diversos sistemas operacionais sem necessitar de dependências externas. Para exemplificar, vamos fazer a importação do conjuto de dados Leukocyte_Profiles armazenado em uma planilha do Excel utilizando o readxl, isto é,

# Definir o diretório onde se encontram os dados

setwd('datasets')

# Instalação e leitura do pacote

# install.packages("readxl")
library("readxl")
## Warning: package 'readxl' was built under R version 4.2.2
# Leitura dos dados pela função read_excel():

# read_excel(path, sheet = NULL,range = NULL, col_names = TRUE,
#            col_types = NULL, na = "", trim_ws = TRUE, skip = 0,
#            n_max = Inf, guess_max = min(1000, n_max), 
#            progress = readxl_progress(), .name_repair = "unique")

db.leukocyte <- read_excel('Leukocyte_Profiles.xlsx')

# Visualização dos dados

head(db.leukocyte, n = 10)
## # A tibble: 10 × 9
##    Species       Fat s…¹ Body …² Heter…³ Lymph…⁴ Eosin…⁵ Monoc…⁶ Basop…⁷ H/L r…⁸
##    <chr>           <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
##  1 Actitis hypo…       5      56      64      19       5       0      12  3.37  
##  2 Actitis hypo…       1      44      60      34       0       1       5  1.76  
##  3 Actitis hypo…       2      46      63      28       5       0       4  2.25  
##  4 Actitis hypo…       3      47       2      87       4       2       5  0.0230
##  5 Actitis hypo…       3      44      68      24       6       0       2  2.83  
##  6 Actitis hypo…       5      45      42      45       6       3       4  0.933 
##  7 Actitis hypo…       3      44      43      42       6       0       9  1.02  
##  8 Actitis hypo…       3      39      40      37       5       1      17  1.08  
##  9 Actitis hypo…       3      44      39      40      18       1       2  0.975 
## 10 Actitis hypo…       3      49      67      25       2       3       3  2.68  
## # … with abbreviated variable names ¹​`Fat score`, ²​`Body mass (g)`,
## #   ³​`Heterophils (H)`, ⁴​`Lymphocytes (L)`, ⁵​Eosinophils, ⁶​Monocytes,
## #   ⁷​Basophils, ⁸​`H/L ratio`
# Estrutura dos dados

str(db.leukocyte)
## tibble [415 × 9] (S3: tbl_df/tbl/data.frame)
##  $ Species        : chr [1:415] "Actitis hypoleucos" "Actitis hypoleucos" "Actitis hypoleucos" "Actitis hypoleucos" ...
##  $ Fat score      : num [1:415] 5 1 2 3 3 5 3 3 3 3 ...
##  $ Body mass (g)  : num [1:415] 56 44 46 47 44 45 44 39 44 49 ...
##  $ Heterophils (H): num [1:415] 64 60 63 2 68 42 43 40 39 67 ...
##  $ Lymphocytes (L): num [1:415] 19 34 28 87 24 45 42 37 40 25 ...
##  $ Eosinophils    : num [1:415] 5 0 5 4 6 6 6 5 18 2 ...
##  $ Monocytes      : num [1:415] 0 1 0 2 0 3 0 1 1 3 ...
##  $ Basophils      : num [1:415] 12 5 4 5 2 4 9 17 2 3 ...
##  $ H/L ratio      : num [1:415] 3.368 1.765 2.25 0.023 2.833 ...

2.5. Exportação de Dados

Importar dados em R é certamente importante para o usuário. No entanto, exportar dados do R para outras plataformas também é igualmente importante. Por exemplo, você pode querer exportar os dados do workspace do R para um arquivo Excel, CSV, arquivo de texto ou, até mesmo, PDF.

A principal função para exportar dados é a função write.table(). Assim como a função read.table(), a função write.table() é muito flexível com muitos argumentos para ajudar a personalizar seu comportamento. Como exemplo, vamos considerar uma base de dados, chamada flowers, disponível no link: https://alexd106.github.io/intro2R/data.html. Para iniciar, então, vamos importar os dados e ordenar as linhas da base de dados em ordem crescente de altura dentro de cada nível de nitrogênio. Isto é,

# Definir o diretório onde se encontram os dados

setwd('datasets')

# Leitura dos dados pela função read.csv():

# read.csv(file, header = TRUE, sep = ",", quote = "\"",
#         dec = ".", fill = TRUE, comment.char = "", ...)

flowers <- read.csv('flower.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(flowers, n = 10)
##    treat nitrogen block height weight leafarea shootarea flowers
## 1    tip   medium     1    7.5   7.62     11.7      31.9       1
## 2    tip   medium     1   10.7  12.14     14.1      46.0      10
## 3    tip   medium     1   11.2  12.76      7.1      66.7      10
## 4    tip   medium     1   10.4   8.78     11.9      20.3       1
## 5    tip   medium     1   10.4  13.58     14.5      26.9       4
## 6    tip   medium     1    9.8  10.08     12.2      72.7       9
## 7    tip   medium     1    6.9  10.11     13.2      43.1       7
## 8    tip   medium     1    9.4  10.28     14.0      28.5       6
## 9    tip   medium     2   10.4  10.48     10.5      57.8       5
## 10   tip   medium     2   12.3  13.48     16.1      36.9       8
# Ordenação dos dados

flowers_df2 <- flowers[order(flowers$nitrogen, flowers$height), ]
head(flowers_df2)
##    treat nitrogen block height weight leafarea shootarea flowers
## 68 notip     high     1    1.2  18.24     16.6     148.1       7
## 72 notip     high     1    2.1  19.15     15.6     176.7       6
## 69 notip     high     1    2.6  16.57     17.1     141.1       3
## 76 notip     high     2    2.6  18.88     16.4     181.5      14
## 79 notip     high     2    4.6  14.65     16.7      91.7      11
## 73 notip     high     2    4.7  13.42     19.8     124.7       5

Agora, vamos aplicar uma transformação de raiz quadrada na variável número de flores (variável flowers) e uma transformação log10 na variável altura (variável height), salvando essas transformações em colunas adicionais na nossa base de dados. Isto é,

# Transformações:

flowers_df2$flowers_sqrt <- sqrt(flowers_df2$flowers) # Raiz Quadrada
flowers_df2$log10_height <- log10(flowers_df2$height) # Log10

# Estrutura dos dados:

str(flowers_df2)
## 'data.frame':    96 obs. of  10 variables:
##  $ treat       : chr  "notip" "notip" "notip" "notip" ...
##  $ nitrogen    : chr  "high" "high" "high" "high" ...
##  $ block       : int  1 1 1 2 2 2 2 2 2 2 ...
##  $ height      : num  1.2 2.1 2.6 2.6 4.6 4.7 5 5.2 6 6.2 ...
##  $ weight      : num  18.2 19.1 16.6 18.9 14.6 ...
##  $ leafarea    : num  16.6 15.6 17.1 16.4 16.7 19.8 17.3 19.1 16.2 11.6 ...
##  $ shootarea   : num  148.1 176.7 141.1 181.5 91.7 ...
##  $ flowers     : int  7 6 3 14 11 5 15 8 2 5 ...
##  $ flowers_sqrt: num  2.65 2.45 1.73 3.74 3.32 ...
##  $ log10_height: num  0.0792 0.3222 0.415 0.415 0.6628 ...

Agora podemos exportar nosso novo banco de dados, denominado de flowers_df2 usando a função write.table(). Neste caso, o primeiro argumento é a base de dados que você deseja exportar (flowers_df2 em nosso exemplo). O segundo argumento é o nome do arquivo (com a extensão do arquivo) e o caminho do arquivo entre aspas simples ou duplas (file = 'caminho'). Como terceiro argumento, tem-se o argumento col.names = TRUE que indica que os nomes das variáveis devem ser escritos na primeira linha do arquivo e, como quarto argumento, o argumento row.names = FALSE que, neste caso, impede R de incluir os nomes das linhas na primeira coluna do arquivo. Por fim, o último argumento, o argumento sep = ";" indica que o separador/delimitador que deve ser utilizado no arquivo exportado (no nosso caso, iremos trabalhar com “;”). Em outras palavras, trabalhamos com a seguinte rotina em R:

## Exportação de dados

# Uso da função 'write.table()'

# write.table(x, file = "", append = FALSE, quote = TRUE, sep = " ",
#            eol = "\n", na = "NA", dec = ".", row.names = TRUE,
#            col.names = TRUE, qmethod = c("escape", "double"),
#            fileEncoding = "")

write.table(flowers_df2, file = 'datasets/flowers_df2.txt', col.names = TRUE, row.names = FALSE, sep = "\t")

3. Análise Exploratória de Dados

3.1. Introdução

Somos frequentemente lembrados do fato que estamos vivendo na era da informação. Sendo assim, nosso foco, é entender como as informações são obtidas, como são analisadas, e como são interpretadas. Na literatura, dá-se o nome de Estatística à ciência que estuda dados e informações. Basicamente, nossos objetivos são dois: (1) entender as principais ferramentas para organizar e resumir dados (Estatística Descritiva), e (2) entender as principais ferramentas para o auxilio de tomada de decisões sobre um grande volume de dados (Inferência Estatística).

Para recapitular brevemente, a Estatística Descritiva (no sentido amplo do termo) é um ramo da estatística que tem por objetivo resumir, descrever e apresentar uma série de valores ou um conjunto de dados, sendo frequentemente o primeiro passo e uma parte importante em qualquer análise estatística, pois permite verificar a qualidade dos dados e ajuda a “entender” os dados, tendo uma visão geral clara dos mesmos. Se bem apresentada, a estatística descritiva já é um bom ponto de partida para futuras análises.

3.2. Distribuição de Frequências

Quando trabalhamos com dados, em geral, não desejamos trabalhar com os dados brutos, pois as dimensões podem ser gigantescas. Neste caso, podemos trabalhar com a distribuição de frequências que é uma ferramenta útil para resumir tais dados, sejam eles qualitativos ou quantitativos. Neste contexto, para construir a distribuição de frequências completa dos dados, temos cinco conceitos estatísticos importantes: frequência absoluta, frequência absoluta acumulada, frequência relativa, frequência relativa em percentual, e frequência relativa acumulada.

3.2.1. Variáveis Qualitativas

A frequência absoluta nada mais que os valores observados de uma determinada categoria. Essa frequência é denotada por \(f_a\). Já a frequência absoluta acumulada \((F_a)\) é obtida somando-se a frequência absoluta do valor considerado, às frequências absolutas anteriores a este mesmo valor. Por outro lado, a frequência relativa, indica a proporção de observações pertencentes a uma classe em relação ao total \(n\), e é determinada pela equação:

\[f_r = \dfrac{f_a}{n}\]frequência relativa em percentual \((f_r(\%))\) representa o percentual de observações a uma classe é obtida simplesmente multiplicando-se a frequência relativa por 100. Por fim, a frequência relativa acumulada \((F_r)\) é obtida da mesma forma que a frequência absoluta acumulada.

Para trabalhar com tabelas de frequência no R, podemos fazer o uso do pacote summarytools através da função freq(). A função freq() produz tabelas de frequência com frequências, proporções, bem como informações de dados ausentes. Para exemplificar o funcionamento da função, vamos considerar a base de dados heart descrita anteriormente.

## Leitura dos Dados (Dataset: Heart)

setwd('datasets')

db <- read.csv('heart.csv', header = TRUE, sep = ',')

## Reestruturação do Banco de Dados

db$Event    <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender   <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking  <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP       <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia  <- ifelse(db$Anaemia == 1, 'Yes', 'No')

# Visualização dos dados

head(db, n = 10)
##    Time        Event Gender Smoking Diabetes  BP Anaemia Age Ejection.Fraction
## 1    97     Censored Female      No       No  No     Yes  43                50
## 2   180     Censored   Male     Yes      Yes  No     Yes  73                30
## 3    31 Non-Censored   Male     Yes       No Yes      No  70                20
## 4    87     Censored   Male      No       No  No     Yes  65                25
## 5   113     Censored   Male      No       No  No      No  64                60
## 6    10 Non-Censored   Male      No       No  No     Yes  75                15
## 7   250     Censored   Male     Yes       No  No      No  70                40
## 8    27 Non-Censored   Male      No      Yes Yes      No  94                38
## 9    87     Censored   Male      No       No Yes      No  75                45
## 10   87     Censored   Male     Yes       No  No      No  80                25
##    Sodium Creatinine Pletelets  CPK
## 1     135       1.30    237000  358
## 2     142       1.18    160000  231
## 3     134       1.83    263358  582
## 4     141       1.10    298000  305
## 5     137       1.00    242000 1610
## 6     137       1.20    127000  246
## 7     136       2.70     51000  582
## 8     134       1.83    263358  582
## 9     137       1.18    263358  582
## 10    144       1.10    149000  898
# Instalação e leitura do pacote

# install.packages("summarytools")
library(summarytools)

# Uso da função 

# freq(x, var = NULL, round.digits = st_options("round.digits"), order = "default",
#     style = st_options("style"), plain.ascii = st_options("plain.ascii"), justify = "default",
#     cumul = st_options("freq.cumul"), totals = st_options("freq.totals"), 
#     report.nas = st_options("freq.report.nas"), rows = numeric(), missing = "", 
#     display.type = TRUE, display.labels = st_options("display.labels"),
#     headings = st_options("headings"), weights = NA, rescale.weights = FALSE, ...)

freq(db$Smoking)
## Frequencies  
## db$Smoking  
## Type: Character  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##          No    203     67.89          67.89     67.89          67.89
##         Yes     96     32.11         100.00     32.11         100.00
##        <NA>      0                               0.00         100.00
##       Total    299    100.00         100.00    100.00         100.00

Uma outra opção é fazer o uso do pacote epiDisplay através da função tab1(). A função tab1() é uma tabulação unidirecional avançada que fornece uma boa tabela de frequências e também um gráfico de barras. A descrição da variável também é usada no título principal do gráfico.

# Instalação e leitura do pacote

# install.packages("epiDisplay")
library(epiDisplay)
## Warning: package 'epiDisplay' was built under R version 4.2.2
## Loading required package: foreign
## Loading required package: survival
## Loading required package: MASS
## Loading required package: nnet
# Uso da Função:

# tab1(x0, decimal = 1, sort.group = FALSE, cum.percent = !any(is.na(x0)), graph = TRUE, missing = TRUE, 
#     bar.values = "frequency", horiz = FALSE, cex = 1, cex.names = 1, main = "auto", xlab = "auto", 
#     ylab = "auto", col = "auto", gen.ind.vars = FALSE, ...) 

tab1(db$Smoking, sort.group = "decreasing", cum.percent = TRUE, main = 'Distribuição de Frequências: Fumo')

## db$Smoking : 
##         Frequency Percent Cum. percent
## No            203    67.9         67.9
## Yes            96    32.1        100.0
##   Total       299   100.0        100.0

3.2.2. Variáveis Quantitativas

Vamos supor, agora, que nossos dados sejam de natureza quantitativa contínua em vez de natureza qualitativa. Como a natureza dos dados agora é contínua, devemos trabalhar com intervalos de classe (transformando-os em categorias) e, então, utiliza-se da distribuição de frequências. Para classificar, então, os dados de natureza contínua em intervalos de classe, primeiramente, deve-se definir o número de classes que será utilizado. Como regra geral, é recomendado utilizar entre 5 a 20 classes dependendo da magnitude dos dados. Em algumas situações, no entanto, pode-se trabalhar com uma metodologia conhecida como Regra de Sturges para definir o número de classes. A regra de Sturges é descrita pela equação:

\[k = 1 + 3.3 \cdot \log(n)\]

em que \(k\) é o número total de classes e \(n\) é o tamanho da amostra. Uma vez definido o número de classes, precisamos também definir à amplitude da classe que nos diz o quão grande deve ser nosso intervalo de classe. Tal amplitude é descrita, matematicamente, por:

\[A =\dfrac{\text{Maior valor observado} - \text{Menor valor observado}}{k}\]

Agora, conhecendo o número e à amplitude da classe, precisamos definir o que chamamos de limites de classe. Os limites da classe devem ser escolhidos de modo que cada observação pertença a uma e apenas uma classe, e são divididos em: limite inferior \((l_i)\), e limite superior \((L_i)\).

Para trabalhar com tabelas de frequência para variáveis quantitativas no R, pode-se trabalhar com o pacote agricolae utilizando-se as funções graph.freq() e table.freq() que criam os intervalos de classe com base na fórmula de Sturges. Para exemplificar o funcionamento da função, vamos considerar a base de dados growth disponível no pacote agricolae do R.

# Instalação e leitura do pacote

# install.packages("agricolae")
library(agricolae)
## Warning: package 'agricolae' was built under R version 4.2.2
# Leitura dos dados

data(growth)
db.growth <- growth

# Visualização dos dados

head(db.growth, n = 10)
##    place slime height
## 1     L1     6    9.4
## 2     L1     7    9.2
## 3     L1     7   10.2
## 4     L1     6   12.5
## 5     L1    10   12.2
## 6     L1     9   10.5
## 7     L1    17   12.1
## 8     L1    17   10.2
## 9     L1    17   11.4
## 10    L1    18   10.1
# Uso da função graph.freq()

# graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
#            xlab="",ylab="",axes = "",las=1,...)

tab <- with(growth, graph.freq(height, plot = FALSE))

# Uso da função table.freq()

# table.freq(object)

print(table.freq(tab),row.names=FALSE)
##  Lower Upper Main Frequency Percentage CF   CPF
##    6.0   7.2  6.6         1        3.3  1   3.3
##    7.2   8.4  7.8         0        0.0  1   3.3
##    8.4   9.6  9.0         4       13.3  5  16.7
##    9.6  10.8 10.2         8       26.7 13  43.3
##   10.8  12.0 11.4        11       36.7 24  80.0
##   12.0  13.2 12.6         6       20.0 30 100.0

3.2.3. Múltiplas Tabelas de Frequência: Uso de Laço de Repetição

Em muitas situações, trabalhar com tabelas de frequências pode ser um pouco extenso quando há mais de uma variável para construir as mesmas. Nestes casos, com o objetivo de facilitar o trabalho, podemos, a partir do conceito de laço de repetição e a estrutura de dados de lista, construir as tabelas de frequência para todas as variáveis de uma única vez, em vez de uma a uma como normalmente se faz. Sendo assim, para o caso das variáveis qualitativas, seguimos a rotina:

## Leitura dos Dados (Dataset: Heart)

setwd('datasets')

db <- read.csv('heart.csv', header = TRUE, sep = ',')

head(db) # Visuzalição dos dados
##   Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1   97     0      0       0        0  0       1  43                50    135
## 2  180     0      1       1        1  0       1  73                30    142
## 3   31     1      1       1        0  1       0  70                20    134
## 4   87     0      1       0        0  0       1  65                25    141
## 5  113     0      1       0        0  0       0  64                60    137
## 6   10     1      1       0        0  0       1  75                15    137
##   Creatinine Pletelets  CPK
## 1       1.30    237000  358
## 2       1.18    160000  231
## 3       1.83    263358  582
## 4       1.10    298000  305
## 5       1.00    242000 1610
## 6       1.20    127000  246
dim(db)  # Dimensão dos dados
## [1] 299  13
str(db)  # Estrutura dos dados
## 'data.frame':    299 obs. of  13 variables:
##  $ Time             : int  97 180 31 87 113 10 250 27 87 87 ...
##  $ Event            : int  0 0 1 0 0 1 0 1 0 0 ...
##  $ Gender           : int  0 1 1 1 1 1 1 1 1 1 ...
##  $ Smoking          : int  0 1 1 0 0 0 1 0 0 1 ...
##  $ Diabetes         : int  0 1 0 0 0 0 0 1 0 0 ...
##  $ BP               : int  0 0 1 0 0 0 0 1 1 0 ...
##  $ Anaemia          : int  1 1 0 1 0 1 0 0 0 0 ...
##  $ Age              : num  43 73 70 65 64 75 70 94 75 80 ...
##  $ Ejection.Fraction: int  50 30 20 25 60 15 40 38 45 25 ...
##  $ Sodium           : int  135 142 134 141 137 137 136 134 137 144 ...
##  $ Creatinine       : num  1.3 1.18 1.83 1.1 1 1.2 2.7 1.83 1.18 1.1 ...
##  $ Pletelets        : num  237000 160000 263358 298000 242000 ...
##  $ CPK              : int  358 231 582 305 1610 246 582 582 582 898 ...
## Reestruturação do Banco de Dados

db$Event    <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender   <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking  <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP       <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia  <- ifelse(db$Anaemia == 1, 'Yes', 'No')

## Tabelas de Frequência: Variáveis Qualitativas

library(summarytools)

# Variáveis Qualitativas: Colunas 2 à 7

tabs.quali <- list()

for(i in 2:7)
{
    tabs.quali[[i - 1]] <- freq(db[,i])
}

tabs.quali
## Frequencies  
## 
##                      Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ------------------ ------ --------- -------------- --------- --------------
##           Censored    203     67.89          67.89     67.89          67.89
##       Non-Censored     96     32.11         100.00     32.11         100.00
##               <NA>      0                               0.00         100.00
##              Total    299    100.00         100.00    100.00         100.00
## 
## Type: Character  
## 
##                Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ------------ ------ --------- -------------- --------- --------------
##       Female    105     35.12          35.12     35.12          35.12
##         Male    194     64.88         100.00     64.88         100.00
##         <NA>      0                               0.00         100.00
##        Total    299    100.00         100.00    100.00         100.00
## 
## Type: Character  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##          No    203     67.89          67.89     67.89          67.89
##         Yes     96     32.11         100.00     32.11         100.00
##        <NA>      0                               0.00         100.00
##       Total    299    100.00         100.00    100.00         100.00
## 
## Type: Character  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##          No    174     58.19          58.19     58.19          58.19
##         Yes    125     41.81         100.00     41.81         100.00
##        <NA>      0                               0.00         100.00
##       Total    299    100.00         100.00    100.00         100.00
## 
## Type: Character  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##          No    194     64.88          64.88     64.88          64.88
##         Yes    105     35.12         100.00     35.12         100.00
##        <NA>      0                               0.00         100.00
##       Total    299    100.00         100.00    100.00         100.00
## 
## Type: Character  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##          No    170     56.86          56.86     56.86          56.86
##         Yes    129     43.14         100.00     43.14         100.00
##        <NA>      0                               0.00         100.00
##       Total    299    100.00         100.00    100.00         100.00

Por outro lado, para o caso das variáveis contínuas, podemos seguir a seguinte rotina:

## Tabelas de Frequência: Variáveis Quantitativas

library(agricolae)

# Variáveis Qualitativas: Colunas 1, 8 à 13

tabs.quanti <- list()

for(i in c(1,8:13))
{
    tabs.quanti[[i]] <- table.freq(graph.freq(db[,i], plot = FALSE))
}

tabs.quanti
## [[1]]
##   Lower Upper Main Frequency Percentage  CF   CPF
## 1     0    32   16        41       13.7  41  13.7
## 2    32    64   48        24        8.0  65  21.7
## 3    64    96   80        60       20.1 125  41.8
## 4    96   128  112        39       13.0 164  54.8
## 5   128   160  144        19        6.4 183  61.2
## 6   160   192  176        30       10.0 213  71.2
## 7   192   224  208        44       14.7 257  86.0
## 8   224   256  240        31       10.4 288  96.3
## 9   256   288  272        11        3.7 299 100.0
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## NULL
## 
## [[5]]
## NULL
## 
## [[6]]
## NULL
## 
## [[7]]
## NULL
## 
## [[8]]
##    Lower Upper  Main Frequency Percentage  CF   CPF
## 1   40.0  46.1 43.05        40       13.4  40  13.4
## 2   46.1  52.2 49.15        43       14.4  83  27.8
## 3   52.2  58.3 55.25        42       14.0 125  41.8
## 4   58.3  64.4 61.35        59       19.7 184  61.5
## 5   64.4  70.5 67.45        63       21.1 247  82.6
## 6   70.5  76.6 73.55        22        7.4 269  90.0
## 7   76.6  82.7 79.65        16        5.4 285  95.3
## 8   82.7  88.8 85.75         8        2.7 293  98.0
## 9   88.8  94.9 91.85         4        1.3 297  99.3
## 10  94.9 101.0 97.95         2        0.7 299 100.0
## 
## [[9]]
##    Lower Upper  Main Frequency Percentage  CF   CPF
## 1   14.0  21.3 17.65        23        7.7  23   7.7
## 2   21.3  28.6 24.95        36       12.0  59  19.7
## 3   28.6  35.9 32.25        83       27.8 142  47.5
## 4   35.9  43.2 39.55        77       25.8 219  73.2
## 5   43.2  50.5 46.85        41       13.7 260  87.0
## 6   50.5  57.8 54.15         3        1.0 263  88.0
## 7   57.8  65.1 61.45        34       11.4 297  99.3
## 8   65.1  72.4 68.75         1        0.3 298  99.7
## 9   72.4  79.7 76.05         0        0.0 298  99.7
## 10  79.7  87.0 83.35         1        0.3 299 100.0
## 
## [[10]]
##    Lower Upper Main Frequency Percentage  CF   CPF
## 1    110   114  112         1        0.3   1   0.3
## 2    114   118  116         1        0.3   2   0.7
## 3    118   122  120         1        0.3   3   1.0
## 4    122   126  124         2        0.7   5   1.7
## 5    126   130  128         8        2.7  13   4.3
## 6    130   134  132        38       12.7  51  17.1
## 7    134   138  136       126       42.1 177  59.2
## 8    138   142  140        92       30.8 269  90.0
## 9    142   146  144        28        9.4 297  99.3
## 10   146   150  148         2        0.7 299 100.0
## 
## [[11]]
##   Lower Upper  Main Frequency Percentage  CF   CPF
## 1  0.50  1.49 0.995       227       75.9 227  75.9
## 2  1.49  2.48 1.985        49       16.4 276  92.3
## 3  2.48  3.47 2.975        11        3.7 287  96.0
## 4  3.47  4.46 3.965         6        2.0 293  98.0
## 5  4.46  5.45 4.955         1        0.3 294  98.3
## 6  5.45  6.44 5.945         2        0.7 296  99.0
## 7  6.44  7.43 6.935         1        0.3 297  99.3
## 8  7.43  8.42 7.925         0        0.0 297  99.3
## 9  8.42  9.41 8.915         2        0.7 299 100.0
## 
## [[12]]
##     Lower  Upper   Main Frequency Percentage  CF   CPF
## 1   20000 112000  66000         9        3.0   9   3.0
## 2  112000 204000 158000        58       19.4  67  22.4
## 3  204000 296000 250000       148       49.5 215  71.9
## 4  296000 388000 342000        57       19.1 272  91.0
## 5  388000 480000 434000        18        6.0 290  97.0
## 6  480000 572000 526000         6        2.0 296  99.0
## 7  572000 664000 618000         1        0.3 297  99.3
## 8  664000 756000 710000         1        0.3 298  99.7
## 9  756000 848000 802000         0        0.0 298  99.7
## 10 848000 940000 894000         1        0.3 299 100.0
## 
## [[13]]
##   Lower Upper Main Frequency Percentage  CF   CPF
## 1    20   892  456       255       85.3 255  85.3
## 2   892  1764 1328        20        6.7 275  92.0
## 3  1764  2636 2200        14        4.7 289  96.7
## 4  2636  3508 3072         3        1.0 292  97.7
## 5  3508  4380 3944         2        0.7 294  98.3
## 6  4380  5252 4816         2        0.7 296  99.0
## 7  5252  6124 5688         1        0.3 297  99.3
## 8  6124  6996 6560         0        0.0 297  99.3
## 9  6996  7868 7432         2        0.7 299 100.0

3.3. Representações Gráficas

Uma segunda maneira de resumir e exibir dados é por meio do uso de representações gráficas. Em geral, os gráficos devem ser projetados de modo que transmitam os padrões em um conjunto de observações em um único olhar. No entanto, embora sejam mais fáceis de ler do que tabelas, os gráficos geralmente fornecem um grau menor de detalhes, mas também devem ser simples e autoexplicativos.

3.3.1. Gráfico de Barras

Os gráficos de barras são um tipo popular de gráfico usado para exibir uma distribuição de frequência para dados nominais, ordinais ou discretos. Em um gráfico de barras, as categorias nas quais as observações se enquadram são apresentadas em um eixo horizontal. Em seguida, uma barra vertical é desenhada acima de cada categoria, de modo que a altura da barra represente a frequência absoluta ou relativa das observações dentro dessa categoria. As barras devem ter a mesma largura e separadas umas das outras para não implicar continuidade. No R, o gráfico de barras é obtido pela função barplot(). Sendo assim, considerando a base de dados mtcars disponível no R, podemos, por exemplo, obter o gráfico de barras da variável cyl referente ao número de cilindros dos carros registrados pela rotina:

# Leitura dos dados

data(mtcars)
db.mtcars <- mtcars 

# Visualização dos dados

head(db.mtcars, n = 10)
##                    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## Duster 360        14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D         24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230          22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 280          19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
# Gráfico de barras da variável 'cyl'

# Uso da função barplot()

# barplot(height, width = 1, space = NULL,
#        names.arg = NULL, legend.text = NULL, beside = FALSE,
#        horiz = FALSE, density = NULL, angle = 45,
#        col = NULL, border = par("fg"),
#        main = NULL, sub = NULL, xlab = NULL, ylab = NULL,
#        xlim = NULL, ylim = NULL, xpd = TRUE, log = "",
#        axes = TRUE, axisnames = TRUE,
#        cex.axis = par("cex.axis"), cex.names = par("cex.axis"),
#        inside = TRUE, plot = TRUE, axis.lty = 0, offset = 0,
#        add = FALSE, ann = !add && par("ann"), args.legend = NULL, ...)

cyl <- table(db.mtcars$cyl)
barplot(cyl, main = 'Gráfico de Barras: Nº de Cilindros')

Como segundo exemplo, consideremos nossa base de dados heart com o objetivo de se obter o gráfico de barras da variável Pletelets referente ao número de plaquetas dos pacientes com insufiência cardíaca. Observe que a escala do número de plaquetas é muito alta, então, por questões de melhor visualização, iremos trabalhar com notação científica e apenas 15 pacientes, neste caso. Logo, procedemos pela seguinte rotina:

# Leitura dos dados

setwd('datasets')

db <- read.csv('heart.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(db, n = 10)
##    Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1    97     0      0       0        0  0       1  43                50    135
## 2   180     0      1       1        1  0       1  73                30    142
## 3    31     1      1       1        0  1       0  70                20    134
## 4    87     0      1       0        0  0       1  65                25    141
## 5   113     0      1       0        0  0       0  64                60    137
## 6    10     1      1       0        0  0       1  75                15    137
## 7   250     0      1       1        0  0       0  70                40    136
## 8    27     1      1       0        1  1       0  94                38    134
## 9    87     0      1       0        0  1       0  75                45    137
## 10   87     0      1       1        0  0       0  80                25    144
##    Creatinine Pletelets  CPK
## 1        1.30    237000  358
## 2        1.18    160000  231
## 3        1.83    263358  582
## 4        1.10    298000  305
## 5        1.00    242000 1610
## 6        1.20    127000  246
## 7        2.70     51000  582
## 8        1.83    263358  582
## 9        1.18    263358  582
## 10       1.10    149000  898
# Gráfico de barras da variável 'Pletelets'

db1 <- db[1:15,]

par(mar = c(5,5,5,1))
barplot(db1$Pletelets, 
        axes = FALSE, 
        names.arg = 1:15, 
        xlab = 'ID do Paciente',
        main = 'Gráfico de barras referente ao número de plaquetas \n dos pacientes com insufiência cardiáca',
        ylim = c(0, 350000),
        ylab = 'Número de Plaquetas (x 10³)',
        cex.lab = 1.2)
axis(side = 2, at = seq(0, 350000, 50000), labels = seq(0, 350, 50), las = 1)

3.3.2. Gráfico de Setores

Os gráficos de setores apresentam as mesmas informações que os gráficos de barras, mas na forma de um círculo ou “pizza”. Neste tipo gráfico, o círculo é dividido em fatias, uma para cada categoria de dados, e o tamanho de cada fatia é determinado pela sua medição angular. Isto é, uma vez que um círculo contém 360°, uma fatia que contém, por exemplo, 50% dos casos teria uma medida angular de 180°. Como regra geral, se a fatia contém \(0\% < \alpha < 100\%\) dos casos, o ângulo para a fatia será proporcional à \(360º \cdot \alpha\). No R, o gráfico de setores é obtido pela função pie(). Sendo assim, considerando a base de dados mtcars disponível no R, podemos, por exemplo, obter o gráfico de setores da variável cyl referente ao número de cilindros dos carros registrados pela rotina:

# Leitura dos dados

data(mtcars)
db.mtcars <- mtcars 

# Visualização dos dados

head(db.mtcars, n = 10)
##                    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## Duster 360        14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D         24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230          22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 280          19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
# Gráfico de setores da variável 'cyl'

# Uso da função pie()

# pie(x, labels = names(x), edges = 200, radius = 0.8,
#    clockwise = FALSE, init.angle = if(clockwise) 90 else 0,
#    density = NULL, angle = 45, col = NULL, border = NULL,
#    lty = NULL, main = NULL, ...)

# Uso da função legend()

# legend(x, y,   # Coordinates (x also accepts keywords)
#       legend, # Vector with the name of each group
#       fill,   # Creates boxes in the legend with the specified colors
#       col = par("col"), # Color of lines or symbols
#       border = "black", # Fill box border color
#       lty, lwd,         # Line type and width
#       pch,              # Add pch symbols to legend lines or boxes
#       bty = "o",        # Box type (bty = "n" removes the box)
#       bg = par("bg")    # Background color of the legend
#       box.lwd = par("lwd"), # Legend box line width
#       box.lty = par("lty"), # Legend box line type
#       box.col = par("fg"),  # Legend box line color
#       cex = 1,          # Legend size
#       horiz = FALSE     # Horizontal (TRUE) or vertical (FALSE) legend
#       title = NULL      # Legend title
#       )

# Uso da função round():

# round(x, digits = 0)

cyl <- table(db.mtcars$cyl)

cylpercent <- round(100*cyl/sum(cyl),1)

pie(cyl, main = 'Gráfico de Setores: Nº de Cilindros',
    labels = paste(cylpercent, '%'), col = c('darkred', 'gray', 'darkgreen'), border = 'white')

legend('topright', legend = c('4','6','8'), 
       fill = c('darkred', 'gray', 'darkgreen'), 
       title = 'Nº de Cilindros',
       bty = 'n')

Como segundo exemplo, considere as principais causas de morte nos Estados Unidos no ano de 2009. Neste caso, as causas são obtidas pelo vetor death e a frequência absoluta das causas é obtida a partir do comando table. O gráfico de setores, então, pode ser determinado pela seguinte rotina:

## Base de Dados

death <- c(rep('Doenças do Coração', 25), rep('Câncer', 23), rep('Doenças Respiratórias', 6), 
           rep('Acidentes', 5), rep('Alzheimer', 3), rep('Diabetes', 3), rep('Pneumonia', 2), 
           rep('Outras', 34))

## Frequências Absolutas

death <- table(death)

## Frequências Relativas em Porcentagem

death.percent <- round(100*death/sum(death),1)

## Nome das Categorias 

death.names <- c('Acidentes', 'Alzheimer', 'Câncer', 'Diabetes', 'Doenças do Coração', 
                 'Doenças Respiratórias', 'Outras', 'Pneumonia')

## Gráfico

pie(death, 
    main = 'Gráfico de Setores: As principais causas de morte \n nos Estados Unidos em 2009 (Gordis, 2017)', 
    labels = paste(death.names, death.percent, '%'), 
    col = rainbow(8), 
    border = 'white')

3.3.3. Gráfico de Linhas

Os gráficos de linhas mostram as informações como uma série de pontos de dados que estão conectadas por segmentos de linha reta. Este tipo gráfico tem dois eixos: o eixo X que normalmente representa os períodos de tempo e o eixo Y tem um valor quantitativo. Para construir este gráfico, ordena-se os dados em pares em que a primeira coordenada representa a variável temporal e a segunda representa a variável quantitativa de interesse ao estudo. Em seguida, marca-se os pontos relativos os pares de dados, ligando-os com um segmento de reta. No R, este gráfico é obtido pela função plot() com argumento type = 'o'. Para exemplificar, considerando a base de dados Nile disponível no R, podemos obter o gráfico de linhas referente as medições do fluxo anual do rio Nilo em Aswan no período de 1871-1970 pela rotina:

# Leitura dos dados

data(Nile)
db.Nile <- Nile

# Visualização dos dados

head(db.Nile, n = 100)
##   [1] 1120 1160  963 1210 1160 1160  813 1230 1370 1140  995  935 1110  994 1020
##  [16]  960 1180  799  958 1140 1100 1210 1150 1250 1260 1220 1030 1100  774  840
##  [31]  874  694  940  833  701  916  692 1020 1050  969  831  726  456  824  702
##  [46] 1120 1100  832  764  821  768  845  864  862  698  845  744  796 1040  759
##  [61]  781  865  845  944  984  897  822 1010  771  676  649  846  812  742  801
##  [76] 1040  860  874  848  890  744  749  838 1050  918  986  797  923  975  815
##  [91] 1020  906  901 1170  912  746  919  718  714  740
# Gráfico de linhas das medições do fluxo anual do rio Nilo

# Uso da função plot()

# plot(x,y, col = NULL, type = NULL, pch = NULL, lwd = NULL, main = NULL, ...)

plot(db.Nile, type = 'o', lwd = 2, pch = 19, ylab = 'Nile Annual Flow', 
     main = 'Gráfico de Linhas: Fluxo Anual do Rio Nilo')

Como segundo exemplo, considere uma base de dados referentes a COVID-19, denominada db.covid. A COVID-19 é uma doença infecciosa causada pelo coronavírus SARS-CoV-2 e tem como principais sintomas febre, cansaço e tosse seca. Em 26 de novembro de 2021, a OMS designou a variante da COVID-19 B.1.1.529 como uma variante de preocupação denominada Ômicron. Essa variante apresenta um grande número de mutações, algumas das quais preocupantes. As outras variantes de preocupação ainda estão em circulação e são: Alfa, Beta, Gama e Delta. Então, para ilustrar nosso gráfico de linhas, consideremos o número de mortes por COVID-19 nos 15 dias finais do mês de outubro de 2021 no Brasil.

## Base de Dados

data <- as.Date(c('2021-10-16', '2021-10-17', '2021-10-18', '2021-10-19', '2021-10-20', 
          '2021-10-21', '2021-10-22', '2021-10-23', '2021-10-24', '2021-10-25',
          '2021-10-26', '2021-10-27', '2021-10-28', '2021-10-29', '2021-10-30'))
mortes <- c(465,128,202,379,399,403,447,350,119,207,403,435,373,408,216)

db.covid <- data.frame(data, mortes)
names(db.covid) <- c('Data', 'Mortes')

## Visualização dos Dados

head(db.covid)
##         Data Mortes
## 1 2021-10-16    465
## 2 2021-10-17    128
## 3 2021-10-18    202
## 4 2021-10-19    379
## 5 2021-10-20    399
## 6 2021-10-21    403
## Gráfico

plot(x = db.covid$Data, 
     y = db.covid$Mortes, 
     type = 'o', 
     lwd = 2, 
     pch = 19, 
     ylab = 'Número de Mortes por COVID-19', 
     xlab = 'Data',
     main = 'Gráfico de Linhas: Mortes por COVID-19 em outubro de 2021 no Brasil')

3.3.4. Histograma

Talvez o tipo de gráfico mais usado seja o histograma. Enquanto um gráfico de barras é uma representação de uma distribuição de frequência para dados nominais ou ordinais, um histograma descreve uma distribuição de frequência para dados agrupados em classes. Para a construção do histograma, no eixo horizontal, especifique os intervalos de classe. No outro eixo, especifique escala de frequência. Em seguida, usando uma barra de largura fixa desenhada acima de cada intervalo de classe, estenda a barra até atingir a frequência da classe. No R, este gráfico é obtido pela função hist(). Para exemplificar, considerando a base de dados heart, podemos obter o histograma referente aos níveis de sódio fornecidos pela variável Sodium pela rotina:

# Definir o diretório onde se encontram os dados

setwd('datasets')


# Leitura dos dados pela função read.csv():

# read.csv(file, header = TRUE, sep = ",", quote = "\"",
#         dec = ".", fill = TRUE, comment.char = "", ...)

db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(db.heart, n = 10)
##    Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1    97     0      0       0        0  0       1  43                50    135
## 2   180     0      1       1        1  0       1  73                30    142
## 3    31     1      1       1        0  1       0  70                20    134
## 4    87     0      1       0        0  0       1  65                25    141
## 5   113     0      1       0        0  0       0  64                60    137
## 6    10     1      1       0        0  0       1  75                15    137
## 7   250     0      1       1        0  0       0  70                40    136
## 8    27     1      1       0        1  1       0  94                38    134
## 9    87     0      1       0        0  1       0  75                45    137
## 10   87     0      1       1        0  0       0  80                25    144
##    Creatinine Pletelets  CPK
## 1        1.30    237000  358
## 2        1.18    160000  231
## 3        1.83    263358  582
## 4        1.10    298000  305
## 5        1.00    242000 1610
## 6        1.20    127000  246
## 7        2.70     51000  582
## 8        1.83    263358  582
## 9        1.18    263358  582
## 10       1.10    149000  898
# Histograma dos níveis de sódio

# Uso da função hist()

# hist(x, breaks = "Sturges",
#     freq = NULL, probability = !freq,
#     include.lowest = TRUE, right = TRUE,
#     density = NULL, angle = 45, col = NULL, border = NULL,
#     main = paste("Histogram of" , xname),
#     xlim = range(breaks), ylim = NULL,
#     xlab = xname, ylab,
#     axes = TRUE, plot = TRUE, labels = FALSE,
#     nclass = NULL, warn.unused = TRUE, ...)

hist(db.heart$Sodium, main = 'Histograma: Níveis de Sódio', xlab = 'Níveis de Sódio')

3.3.5. Gráfico de Densidade

Um outro tipo gráfico importante que temos é o gráfico de densidade que é uma versão suavizada do histograma e é usado no mesmo conceito, ou seja, para representar a distribuição de uma variável numérica. No R, este gráfico é obtido pela combinação das funções funções plot() e density(). Para exemplificar, considerando a base de dados heart, podemos obter o gráfico de densidade referente aos níveis de sódio fornecidos pela variável Sodium pela rotina:

# Definir o diretório onde se encontram os dados

setwd('datasets')

# Leitura dos dados pela função read.csv():

# read.csv(file, header = TRUE, sep = ",", quote = "\"",
#         dec = ".", fill = TRUE, comment.char = "", ...)

db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(db.heart, n = 10)
##    Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1    97     0      0       0        0  0       1  43                50    135
## 2   180     0      1       1        1  0       1  73                30    142
## 3    31     1      1       1        0  1       0  70                20    134
## 4    87     0      1       0        0  0       1  65                25    141
## 5   113     0      1       0        0  0       0  64                60    137
## 6    10     1      1       0        0  0       1  75                15    137
## 7   250     0      1       1        0  0       0  70                40    136
## 8    27     1      1       0        1  1       0  94                38    134
## 9    87     0      1       0        0  1       0  75                45    137
## 10   87     0      1       1        0  0       0  80                25    144
##    Creatinine Pletelets  CPK
## 1        1.30    237000  358
## 2        1.18    160000  231
## 3        1.83    263358  582
## 4        1.10    298000  305
## 5        1.00    242000 1610
## 6        1.20    127000  246
## 7        2.70     51000  582
## 8        1.83    263358  582
## 9        1.18    263358  582
## 10       1.10    149000  898
# Gráfico de densidade

# Uso da função density()

# density(x, bw = "nrd0", adjust = 1,
#        kernel = c("gaussian", "epanechnikov", "rectangular", 
#                  "triangular", "biweight", "cosine", "optcosine"),
#        weights = NULL, window = kernel, width,
#        give.Rkern = FALSE, subdensity = FALSE,
#        n = 512, from, to, cut = 3, na.rm = FALSE, ...)

plot(density(db.heart$Sodium), main = 'Gráfico de Densidade: Níveis de Sódio', xlab = 'Níveis de Sódio')

3.3.6. Boxplot

Um outro tipo de representação gráfica para o resumo dos dados brutos é o gráfico de Box-Whisker (ou boxplot), que traz uma perspectiva sobre o comportamento e variabilidade dos dados. Este gráfico é uma alternativa ao histograma, sendo utilizado quando queremos representar a distribuição de uma variável quantitativa ou, pelo menos, qualitativa ordinal. Além da variável numérica/ordinal, pode-se incluir uma variável de grupo. No R, este gráfico é obtido pela função boxplot(). Para exemplificar, considerando a base de dados heart, podemos obter o boxplot referente ao níveis de sódio (variável Sodium) de acordo com o status de diabetes (variável Diabetes) pela rotina:

# Definir o diretório onde se encontram os dados

setwd('datasets')


# Leitura dos dados pela função read.csv():

# read.csv(file, header = TRUE, sep = ",", quote = "\"",
#         dec = ".", fill = TRUE, comment.char = "", ...)

db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(db.heart, n = 10)
##    Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1    97     0      0       0        0  0       1  43                50    135
## 2   180     0      1       1        1  0       1  73                30    142
## 3    31     1      1       1        0  1       0  70                20    134
## 4    87     0      1       0        0  0       1  65                25    141
## 5   113     0      1       0        0  0       0  64                60    137
## 6    10     1      1       0        0  0       1  75                15    137
## 7   250     0      1       1        0  0       0  70                40    136
## 8    27     1      1       0        1  1       0  94                38    134
## 9    87     0      1       0        0  1       0  75                45    137
## 10   87     0      1       1        0  0       0  80                25    144
##    Creatinine Pletelets  CPK
## 1        1.30    237000  358
## 2        1.18    160000  231
## 3        1.83    263358  582
## 4        1.10    298000  305
## 5        1.00    242000 1610
## 6        1.20    127000  246
## 7        2.70     51000  582
## 8        1.83    263358  582
## 9        1.18    263358  582
## 10       1.10    149000  898
# Boxplot dos níveis de sódio de acordo com o status de diabetes

# Uso da função boxplot()

# boxplot(formula, data = NULL, .., subset, na.action = NULL,
#        xlab = mklab(y_var = horizontal),
#        ylab = mklab(y_var =!horizontal),
#        add = FALSE, ann = !add, horizontal = FALSE,
#        drop = FALSE, sep = ".", lex.order = FALSE)

# Ou

# boxplot(x, ..., range = 1.5, width = NULL, varwidth = FALSE,
#        notch = FALSE, outline = TRUE, names, plot = TRUE,
#        border = par("fg"), col = NULL, log = "",
#        pars = list(boxwex = 0.8, staplewex = 0.5, outwex = 0.5),
#        ann = !add, horizontal = FALSE, add = FALSE, at = NULL)

boxplot(db.heart$Sodium~db.heart$Diabetes, main = 'Boxplot: Níveis de Sódio', 
        names = c('Sim', 'Não'), ylab = 'Níveis de Sódio', xlab = 'Diabetes', pch = 19)

3.3.7. Diagrama de Dispersão

Um outro tipo de representação gráfica para o resumo dos dados brutos é o diagrama de dispersão que é um gráfico onde pontos no espaço cartesiano XY são usados para representar de maneira simultânea os valores das observações de duas variáveis quantitativas. No R, este gráfico é obtido pela função plot(). Para exemplificar, considerando a base de dados heart, podemos obter o diagrama de dispersão referente ao níveis de creatinina (variável Creatinine) de acordo com os valores de CPK (variável CPK) pela rotina:

# Definir o diretório onde se encontram os dados

setwd('datasets')


# Leitura dos dados pela função read.csv():

# read.csv(file, header = TRUE, sep = ",", quote = "\"",
#         dec = ".", fill = TRUE, comment.char = "", ...)

db.heart <- read.csv('heart.csv', header = TRUE, sep = ',')

# Visualização dos dados

head(db.heart, n = 10)
##    Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium
## 1    97     0      0       0        0  0       1  43                50    135
## 2   180     0      1       1        1  0       1  73                30    142
## 3    31     1      1       1        0  1       0  70                20    134
## 4    87     0      1       0        0  0       1  65                25    141
## 5   113     0      1       0        0  0       0  64                60    137
## 6    10     1      1       0        0  0       1  75                15    137
## 7   250     0      1       1        0  0       0  70                40    136
## 8    27     1      1       0        1  1       0  94                38    134
## 9    87     0      1       0        0  1       0  75                45    137
## 10   87     0      1       1        0  0       0  80                25    144
##    Creatinine Pletelets  CPK
## 1        1.30    237000  358
## 2        1.18    160000  231
## 3        1.83    263358  582
## 4        1.10    298000  305
## 5        1.00    242000 1610
## 6        1.20    127000  246
## 7        2.70     51000  582
## 8        1.83    263358  582
## 9        1.18    263358  582
## 10       1.10    149000  898
# Diagrama de dispersão dos níveis de creatinina de acordo com o CPK

# Uso da função plot()

# plot(x,y, col = NULL, type = NULL, pch = NULL, lwd = NULL, main = NULL, ...)

plot(db.heart$Creatinine~db.heart$CPK, main = 'Diagrama de Dispersão: Níveis de Creatinina vs. CPK', 
        xlab = 'CPK', ylab = 'Níveis de Creatinina', pch = 19)

Como segundo exemplo, consideremos a base de dados iris com o objetivo de se obter o gráfico de dispersão das variáveis Petal.Length referente ao tamanho da pétala da planta, e Sepal.Length referente ao tamanho da sépala. Diferente do primeiro exemplo, neste pode-se verificar uma relação mais linear entre ambas as variáveis.

# Carregar a base de dados

data(iris)

# Visualização dos dados

head(iris, n = 10)
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1           5.1         3.5          1.4         0.2  setosa
## 2           4.9         3.0          1.4         0.2  setosa
## 3           4.7         3.2          1.3         0.2  setosa
## 4           4.6         3.1          1.5         0.2  setosa
## 5           5.0         3.6          1.4         0.2  setosa
## 6           5.4         3.9          1.7         0.4  setosa
## 7           4.6         3.4          1.4         0.3  setosa
## 8           5.0         3.4          1.5         0.2  setosa
## 9           4.4         2.9          1.4         0.2  setosa
## 10          4.9         3.1          1.5         0.1  setosa
# Diagrama de dispersão do tamanho da pétala vs. tamanho da sépala

# Uso da função plot()

# plot(x,y, col = NULL, type = NULL, pch = NULL, lwd = NULL, main = NULL, ...)

plot(iris$Petal.Length~iris$Sepal.Length, main = 'Diagrama de Dispersão: Tamanho da Pétala vs. Tamanho da Sépala', 
        xlab = 'Tamanho da Sépala', ylab = 'Tamanho da Pétala', pch = 19)

3.5.8. Gráficos Interativos

Outro tipo de ferramenta gráfica é trabalhar com gráficos interativos. Um gráfico interativo é uma forma de apresentar dados aos usuários que visitam uma página com animações e customizações, criando uma experiência única para quem deseja conferir certas informações. Portanto, em vez de apenas apresentar um quadro fixo, é possível deixar que cada usuário personalize e interaja como desejar com a imagem. No R, pode-se trabalhar com os pacotes dygraphs, ggplot2, dplyr, hrbrthemes, htmlwidgets e xts para a construção desse tipo gráfico.

3.5.7.1. Pacote: dygraphs

O pacote dygraphs é uma interface R para a biblioteca de gráficos JavaScript ‘dygraphs’ que fornece recursos avançados para gráficos de dados de séries temporais em R, incluindo exibição altamente configurável de séries e eixos e recursos interativos como zoom/pan e realce de séries/pontos.

# Instalar e carregar os pacotes necessários

# install.packages('dygraphs')
# install.packages('xts')
# install.packages('htmlwidgets')
library(dygraphs)
## Warning: package 'dygraphs' was built under R version 4.2.2
library(xts)
## Warning: package 'xts' was built under R version 4.2.2
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(htmlwidgets)

# Criar uma base de dados

data <- data.frame(time = seq(from = Sys.Date() - 40, to = Sys.Date(), by = 1), value = runif(41))

# Verificar a estrutura dos dados

str(data)
## 'data.frame':    41 obs. of  2 variables:
##  $ time : Date, format: "2022-11-22" "2022-11-23" ...
##  $ value: num  0.635 0.652 0.867 0.485 0.278 ...
# Converter para formato XTS

# Uso da função xts():

# xts(x, order.by = index(x), frequency = NULL, ...)

data <- xts(x = data$value, order.by = data$time)
6.5.7.1.1. Gráfico de ‘Linha com Pontos’
# Uso da função dygraph():

# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
#  group = NULL, elementId = NULL, width = NULL, height = NULL)

p <- dygraph(data) %>%
  dyOptions(drawPoints = TRUE, pointSize = 4)
p
# Para salvar o gráfico interativo

# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/dygraphs317-1.html"))
6.5.7.1.2. Gráfico de ‘Área’
# Uso da função dygraph():

# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
#  group = NULL, elementId = NULL, width = NULL, height = NULL)

p <- dygraph(data) %>%
  dyOptions(fillGraph=TRUE)
p
6.5.7.1.3. Gráfico de ‘Step’
# Uso da função dygraph():

# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
#  group = NULL, elementId = NULL, width = NULL, height = NULL)

p <- dygraph(data) %>%
  dyOptions(stepPlot=TRUE, fillGraph=TRUE)
p
6.5.7.1.3. Gráfico de ‘Lollipop’
# Uso da função dygraph():

# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
#  group = NULL, elementId = NULL, width = NULL, height = NULL)

p <- dygraph(data) %>%
  dyOptions( stemPlot=TRUE)
p
6.5.7.1.4. Gráfico de ‘Candlestick’
# Criar uma base de dados

trend <- sin(seq(1,41))+runif(41)
data <- data.frame(
  time=seq(from=Sys.Date()-40, to=Sys.Date(), by=1 ), 
  value1=trend, 
  value2=trend+rnorm(41), 
  value3=trend+rnorm(41), 
  value4=trend+rnorm(41) 
)

# Converter para formato XTS

data <- xts(x = data[,-1], order.by = data$time)

# Grafíco

# Uso da função dygraph():

# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
#  group = NULL, elementId = NULL, width = NULL, height = NULL)

p <- dygraph(data) %>%
  dyCandlestick()
p
# Salvar o gráfico

# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/dygraphs317-5.html"))
6.5.7.1.4. Gráfico de ‘Linhas com Intervalo’
# Criar uma base de dados

trend <- sin(seq(1,41))+runif(41)
data <- data.frame(
  time=seq(from=Sys.Date()-40, to=Sys.Date(), by=1 ), 
  trend=trend, 
  max=trend+abs(rnorm(41)), 
  min=trend-abs(rnorm(41, sd=1))
)

# Converter para formato XTS

data <- xts(x = data[,-1], order.by = data$time)

# Gráfico 

# Uso da função dygraph():

# dygraph(data, main = NULL, xlab = NULL, ylab = NULL, periodicity = NULL,
#  group = NULL, elementId = NULL, width = NULL, height = NULL)

p <- dygraph(data) %>%
  dySeries(c("min", "trend", "max"))
p
# Salvar o gráfico

# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/dygraphs317-6.html"))

3.5.7.2. Pacote: ggplot2

O pacote ggplot2, os gráficos são construídos camada por camada, sendo a primeira delas dada pela função ggplot() (repare que não tem o “2”). Essa função recebe um data frame e cria a camada base do gráfico, o nosso “canvas”, onde acrescentaremos todos os outros elementos (camadas).

# Instalar e carregar os pacotes necessários

# install.packages('ggplot2')
# install.packages('dplyr')
# install.packages('plotly')
# install.packages('hrbrthemes')
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.2.2
## 
## Attaching package: 'ggplot2'
## The following object is masked from 'package:epiDisplay':
## 
##     alpha
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.2.2
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:xts':
## 
##     first, last
## The following object is masked from 'package:MASS':
## 
##     select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(plotly)
## Warning: package 'plotly' was built under R version 4.2.2
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:MASS':
## 
##     select
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
library(hrbrthemes)
## Warning: package 'hrbrthemes' was built under R version 4.2.2
## NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
##       Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
##       if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow
# Criar uma base de dados (disponível no GitHub)

data <- read.table("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered.csv", 
                   header = T)
data$date <- as.Date(data$date)

# Verificar a estrutura dos dados

str(data)
## 'data.frame':    1822 obs. of  2 variables:
##  $ date : Date, format: "2013-04-28" "2013-04-29" ...
##  $ value: num  136 147 147 140 126 ...
6.5.7.2.1. Gráfico de ‘Área’
# Uso da função ggplot():

# ggplot(data = NULL, mapping = aes(), ..., environment = parent.frame())

p <- data %>%
  ggplot( aes(x=date, y=value)) +
    geom_area(fill="#69b3a2", alpha=0.5) +
    geom_line(color="#69b3a2") +
    ylab("bitcoin price ($)") +
    theme_ipsum()

# Uso da função ggplotly():

# ggplotly(p = ggplot2::last_plot(), width = NULL,
#  height = NULL, tooltip = "all", dynamicTicks = FALSE,
#  layerData = 1, originalData = TRUE, source = "A", ...)

p <- ggplotly(p)
p
# Salvar o gráfico

# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/ggplotlyAreachart.html"))
6.5.7.2.2. Gráfico ‘Bubble’
# Criar a base de dados (disponível no GitHub, pelo site do plotly)

data <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv")

# Organizar os dados

data_2007 <- data[which(data$year == 2007),]
data_2007 <- data_2007[order(data_2007$continent, data_2007$country),]
slope <- 0.00005
data_2007$size <- sqrt(data_2007$pop * slope)
colors <- c('#4AC6B7', '#1972A4', '#965F8A', '#FF7070', '#C61951')

# Fazer o gráfico

# Uso da função plot_ly():

# plot_ly(data = data.frame(), ..., type = NULL, name,
#  color, colors = NULL, alpha = NULL, stroke, strokes = NULL,
#  alpha_stroke = 1, size, sizes = c(10, 100), span, spans = c(1, 20),
#  symbol, symbols = NULL, linetype, linetypes = NULL, split,
#  frame, width = NULL, height = NULL, source = "A")

fig <- plot_ly(data_2007, x = ~gdpPercap, y = ~lifeExp, color = ~continent, size = ~size, colors = colors,
        type = 'scatter', mode = 'markers', sizes = c(min(data_2007$size), max(data_2007$size)),
        marker = list(symbol = 'circle', sizemode = 'diameter',
                      line = list(width = 2, color = '#FFFFFF')),
        text = ~paste('Country:', country, '<br>Life Expectancy:', lifeExp, '<br>GDP:', gdpPercap,
                      '<br>Pop.:', pop))
fig <- fig %>% layout(title = 'Life Expectancy vs. Per Capita GDP, 2007',
         xaxis = list(title = 'GDP per capita (2000 dollars)',
                      gridcolor = 'rgb(255, 255, 255)',
                      range = c(2.00, 5.20),
                      type = 'log',
                      zerolinewidth = 1,
                      ticklen = 5,
                      gridwidth = 2),
         yaxis = list(title = 'Life Expectancy (Years)',
                      gridcolor = 'rgb(255, 255, 255)',
                      range = c(36, 92),
                      zerolinewidth = 1,
                      ticklen = 5,
                      gridwith = 2),
         paper_bgcolor = 'rgb(243, 243, 243)',
         plot_bgcolor = 'rgb(243, 243, 243)')

fig
## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

3.4. Medidas Descritivas

Embora tabelas de frequência e gráficos nos forneçam resumos descritivos úteis, eles não são completos. Em certas situações, por exemplo, temos por interesse resumir os dados brutos por meio de certos valores numéricos que, na Estatística, são denominados medidas descritivas. Essas medidas são classificadas essencialmente em três grupos:

  • Posição: ponto em torno do qual se concentram os dados.
  • Dispersão: aponta o grau de variabilidade dos dados.
  • Associação: síntese da relação entre duas variáveis.

3.4.1. Medidas de Posição

As medidas de posição representam, por definição estatística, a tendência das observações de um conjunto de dados de se agruparem, ou centralizarem, em torno de seu ponto central ou do ponto no qual tais observações estão condensadas.Isto é, estas medidas tem por objetivo representar os dados de uma forma mais condensada que uma tabela de frequências, localizando a maior concentração de valores em torno de uma distribuição de frequências. Dentre tais medidas, destacam-se a média, a moda, e a mediana.

3.4.1.1. Média

3.4.1.1.1. Dados Brutos

Dentre as medidas de posição, a média (ou média aritmética) é a mais popular, sendo calculada pela soma de todos os valores observados da variável dividida pelo número total de observações. Matematicamente, a média é descrita por:

\[\bar{x} = \sum_{i=1}^{n}\dfrac{x_i}{n}\]

No R, a média pode ser facilmente calculada pela função mean(). Sendo assim, considerando a base de dados iris disponível no R, podemos obter a média da variável Sepal.Length é obtida pela rotina:

# Uso da função:

# mean(x, na.rm = FALSE, ...)

mean(iris$Sepal.Length)
## [1] 5.843333

Observação: Se houver pelo menos um valor ausente no conjunto de dados, utilize mean(iris$Sepal.Length, na.rm = TRUE) para calcular a média com o NA excluído. Este argumento pode ser usado para a maioria das funções apresentadas nesta classe, não apenas para a média.

3.4.1.1.2. Dados Agrupados

Porém, quando nossos dados são agrupados em classes, o cálculo da média é um pouco diferente do habitual. Então, para o cálculo da média, neste caso, devemos, em primeiro lugar, determinar o ponto médio da classe que é definido pela equação:

\[\bar{x}_{c_i} = \dfrac{L_i + l_i}{2}\]

onde \(c_i\) representa a respectiva classe, e \(l_i\) e \(L_i\) são os limites da classe. Em seguida, multiplica-se cada valor do ponto médio da classe pela frequência de cada classe, e soma-se cada resultado. Por fim, dividimos o valor obtido pela soma das frequências de cada classe. Em termos matemáticos, tem-se que a média é dada por:

\[ \bar{x} = \dfrac{\sum_{i=1}^{n} f_{a_i} \bar{x}_{c_i}}{\sum_{i=1}^{n} f_{a_i}}\]

onde \(f_{a_i}\) representa a frequência absoluta da classe.

No R, neste caso, não há uma função pronta para o cálculo da média para dados agrupados, sendo necessário implentar a mesma. Sendo assim, considerando a base de dados iris disponível no R, podemos obter a média da variável Sepal.Length definida em uma tabela de frequências em intervalos de classe pela rotina:

# Carregar Pacote

library(agricolae)

# Cálculo da Média Agrupada

media_agrupada <- function(x)
{
  # Uso da função graph.freq()

  # graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
  #            xlab="",ylab="",axes = "",las=1,...)

  # Uso da função table.freq()

  # table.freq(object)

  tab   <- table.freq(graph.freq(x, plot = FALSE))
  
  media <- sum(tab$Main * tab$Frequency)/sum(tab$Frequency)
  return(media)
}

media_agrupada(iris$Sepal.Length)
## [1] 5.831467

3.4.1.2. Mediana

3.4.1.2.1. Dados Brutos

A mediana é uma medida de posição descrita pelo valor que ocupa a posição central da série de observações de uma variável, dividindo o conjunto em duas partes iguais. Para seu cálculo, devemos seguir os seguintes passos:

  • Ordenar as observações em ordem crescente ou decrescente.

  • Determinar o número de observações da base de dados. Se tal número for ímpar, a mediana será a posição central. Mas se tal número for par, a mediana será dada pela média das duas posições centrais.

No R, a mediana pode ser facilmente calculada pela função median(). Sendo assim, considerando a base de dados iris disponível no R, podemos obter a mediana da variável Sepal.Length é obtida pela rotina:

# Uso da função:

# median(x, na.rm = FALSE, ...)

median(iris$Sepal.Length)
## [1] 5.8

Uma outra forma de calcular a mediana no R é fazer o uso da função quantile() com argumento probs = 0.5, pois a mediana corresponde ao quartil de 50% que veremos mais adiante. Sendo assim, considerando a base de dados iris disponível no R, podemos obter a mediana da variável Sepal.Length é obtida pela rotina:

# Uso da função:

# quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE,
#         names = TRUE, type = 7, digits = 7, ...)

quantile(iris$Sepal.Length, 0.5)
## 50% 
## 5.8
3.4.1.2.2. Dados Agrupados

Por outro lado, se os dados são agrupados em classes, a mediana é calculada seguindo as etapas: (1) identificar a classe que apresenta a posição central dos dados; (2) calcular a mediana pela equação:

\[\tilde{x} = l_i + \dfrac{h(k − F_{a_{i−1}})}{F_{a_i}} \]

em que \(k = \dfrac{n}{2}\) indica a posição central, \(i\) representa a classe, \(l_i\) é o limite inferior, \(h\) é amplitude, e \(F_{a_i} \geq k\) é a frequência acumulada da classe.

Assim como a média, no R, não há uma função pronta para o cálculo da mediana para dados agrupados, sendo necessário implentar a mesma. Sendo assim, considerando a base de dados iris disponível no R, podemos obter a mediana da variável Sepal.Length definida em uma tabela de frequências em intervalos de classe pela rotina:

# Carregar Pacote

library(agricolae)

# Cálculo da Mediana Agrupada

mediana_agrupada <- function(x)
{
  # Uso da função graph.freq()

  # graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
  #            xlab="",ylab="",axes = "",las=1,...)

  # Uso da função table.freq()

  # table.freq(object)

  tab     <- table.freq(graph.freq(x, plot = FALSE))
  
  # Uso da função which()
  
  # which(x, arr.ind = FALSE, useNames = TRUE)
  
  h       <- min(tab$Upper) - min(tab$Lower) # Amplitude
  k       <- sum(tab$Frequency)/2 # Posição Central
  li      <- tab$Lower[min(which(tab$CPF >= 50.0))] # Limite Inferior (Classe da Mediana)
  Fa_1    <- tab$CF[min(which(tab$CPF >= 50.0)) - 1] # Frequência Acumulada (Anterior a Classe da Mediana)
  Fa      <- tab$CF[min(which(tab$CPF >= 50.0))] # Frequêncua Acumulada (Classe da Mediana)
  
  mediana <- li + (h * (k - Fa_1))/Fa
  return(mediana)
}

mediana_agrupada(iris$Sepal.Length)
## [1] 5.662697

3.4.1.3. Moda

3.4.1.3.1. Dados Brutos

A moda é caracterizada pelo valor que apresenta a maior frequência da variável entre os valores observados. No entanto, no R, não há uma função para encontrar a moda de um conjunto de dados. Para resolver essa questão, podemos trabalhar com duas funções de suma importância, as funções table() e sort(). Sendo assim, considerando a base de dados iris disponível no R, podemos obter a moda da variável Sepal.Length é obtida pela rotina:

# Uso da função table()

# table(..., exclude = if (useNA == "no") c(NA, NaN),
#      useNA = c("no", "ifany", "always"), dnn = list.names(...), 
#      deparse.level = 1)

tab <- table(iris$Sepal.Length) # Frequência das observações

# Uso da função sort()

# sort(x, decreasing = FALSE, ...)

sort(tab, decreasing = TRUE) # Ordena do maior para o menor
## 
##   5 5.1 6.3 5.7 6.7 5.5 5.8 6.4 4.9 5.4 5.6   6 6.1 4.8 6.5 4.6 5.2 6.2 6.9 7.7 
##  10   9   9   8   8   7   7   7   6   6   6   6   6   5   5   4   4   4   4   4 
## 4.4 5.9 6.8 7.2 4.7 6.6 4.3 4.5 5.3   7 7.1 7.3 7.4 7.6 7.9 
##   3   3   3   3   2   2   1   1   1   1   1   1   1   1   1

A função table() fornece o número de ocorrências para cada valor exclusivo e, em seguida, a função sort() com o argumento decreasing = TRUE exibe o número de ocorrências do maior para o menor. A moda, então, da variável Sepal.Length é igual a \(x_m = 5\).

3.4.1.3.2. Dados Agrupados

Se os dados são agrupados em classes, a moda é calculada seguindo as etapas: (1) identificar a classe que apresenta a maior frequência; (2) calcular a moda pela equação:

\[x_m = l_i + \dfrac{h(f_{a_i} - f_{a_{i-i}})}{(f_{a_i} - f_{a_{i-i}}) + (f_{a_i} - f_{a_{i+i}})}\]

em que \(i\) representa a classe, \(l_i\) é o limite inferior da classe, \(h\) é amplitude da classe, e \(f_{a_i}\) é a frequência absoluta da classe.

Assim como a média e a mediana, no R, não há uma função pronta para o cálculo da moda para dados agrupados, sendo necessário implentar a mesma. Sendo assim, considerando a base de dados iris disponível no R, podemos obter a moda da variável Sepal.Length definida em uma tabela de frequências em intervalos de classe pela rotina:

# Carregar Pacote

library(agricolae)

# Cálculo da Mediana Agrupada

moda_agrupada <- function(x)
{
  # Uso da função graph.freq()

  # graph.freq(x, breaks=NULL,counts=NULL,frequency=1, plot=TRUE, nclass=NULL,
  #            xlab="",ylab="",axes = "",las=1,...)

  # Uso da função table.freq()

  # table.freq(object)

  tab     <- table.freq(graph.freq(x, plot = FALSE))
  
  # Uso da função which()
  
  # which.max(x)
  
  h       <- min(tab$Upper) - min(tab$Lower) # Amplitude
  li      <- tab$Lower[which.max(tab$Frequency)] # Limite Inferior (Classe Modal)
  fa_1    <- tab$Frequency[which.max(tab$Frequency) - 1] # Frequência Absoluta (Anterior a Classe Modal)
  fa_2    <- tab$Frequency[which.max(tab$Frequency) + 1] # Frequência Absoluta (Posterior a Classe Modal)
  fa      <- tab$Frequency[which.max(tab$Frequency)] # Frequêncua Absoluta (Classe Modal)
  
  moda <- li + (h * (fa - fa_1))/((fa - fa_1) + (fa - fa_2))
  return(moda)
}

moda_agrupada(iris$Sepal.Length)
## [1] 4.945946

3.4.2. Medidas de Dispersão

3.4.2.1. Amplitude

A amplitude de um conjunto, em Estatística, é a diferença entre o maior elemento desse conjunto e o menor. Em outras palavras, para encontrar a amplitude de uma lista de números, basta subtrair o menor elemento do maior. No R, há duas formas de calcularmos a aplitude, a primeira é trabalhando com as funções min() e max(), e a segunda é trabalhando com a função range(). Para o primeiro caso, considerando a base de dados iris disponível no R, a amplitude da variável Sepal.Length é obtida pela rotina:

# Uso da função min():

# min(..., na.rm = FALSE)

min(iris$Sepal.Length)
## [1] 4.3
# Uso da função max():

# max(..., na.rm = FALSE)

max(iris$Sepal.Length)
## [1] 7.9
# Amplitude

A1 <- max(iris$Sepal.Length) - min(iris$Sepal.Length)
A1
## [1] 3.6

3.4.2.2. Amplitude Interquartil

O primeiro conceito que devemos definir nessa seção é, primeiramente, o conceito de quartis. Os quartis, tem por objetivo dividir uma série de observações de uma variável de um conjunto de dados em quatro partes iguais, e são denominados \(Q_1\) (1º quartil, 25%), \(Q_2\) (2º quartil ou mediana, 50%), e \(Q_3\) (3º quartil, 75%). Para calcular tais quartis, seguimos o mesmo procedimento de cálculo da mediana. No entanto, há um critério de ordem para os cálculos. Isto é, primeiro, calcula-se \(Q_2\) (ou mediana). Em seguida, \(Q_1\) usando o mesmo método aplicado para \(Q_2\), e, por fim, calcula-se \(Q_3\) pelo mesmo método. No R, para trabalhar com esse conceito, se faz o uso da função quantile() com argumento probs = 0.25 para o 1º quartil, probs = 0.50 para o 2º quartil, e, por fim, probs = 0.75 para o 3º quartil. Sendo assim, considerando a base de dados iris disponível no R, podemos obter a mediana da variável Sepal.Length é obtida pela rotina:

# Uso da função:

# quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE,
#         names = TRUE, type = 7, digits = 7, ...)

quantile(iris$Sepal.Length, 0.25) # Primeiro Quartil
## 25% 
## 5.1
quantile(iris$Sepal.Length, 0.50) # Segundo Quartil
## 50% 
## 5.8
quantile(iris$Sepal.Length, 0.75) # Terceiro Quartil
## 75% 
## 6.4

A partir desse conceito dos quartis, estamos aptos a definir a primeira medida de dispersão do nosso estudo que é a amplitude interquartil. A amplitude interquartil de um conjunto, em Estatística, é a diferença entre o terceiro e o primeiro quartil, isto é,

\[A_Q = Q_3 - Q_1\]

A grande vantagem dessa medida é que, diferente da amplitude usual (máximo – mínimo), a amplitude interquartil é mais estável, pois não considera valores mais extremos. Além de uma melhor estabilidade, a amplitude interquartil também nos traz uma informação importante sobre os dados que são os outliers. Os outliers são definidos como valores atípicos das observações de uma variável. Sendo assim, uma observação, \(x_i\), será um outlier inferior se:

\[x_i < Q_1 − 1,5 \cdot A_Q\]

e será um outlier superior se:

\[x_i > Q_3 + 1,5 \cdot A_Q\]

No R, para calcular a amplitude interquartil, podemos trabalhar diretamente com a função IQR(). Assim, considerando a base de dados iris disponível no R, a amplitude interquartil da variável Sepal.Width é obtida pela rotina:

# Uso da função IQR():

# IQR(x, na.rm = FALSE, type = 7)

IQR(iris$Sepal.Width)
## [1] 0.5

No entanto, para o cálculo dos outliers, não há uma função implementada em R. Neste caso, podemos, então, criar uma função para este fim da seguinte forma:

f_outliers <- function(x, inferior = TRUE)
{
  
  # Uso da função IQR():

  # IQR(x, na.rm = FALSE, type = 7)
  
  # Uso da função quantile():
  
  # quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE,
  #       names = TRUE, type = 7, digits = 7, ...)

  # Uso da função which():
  
  # which(x, arr.ind = FALSE, useNames = TRUE)
  
  inf <- x[which(x < quantile(x, p = 0.25) - 1.5 * IQR(x))]
  sup <- x[which(x > quantile(x, p = 0.75) + 1.5 * IQR(x))]
  
  if(inferior == TRUE)
  {
    results <- data.frame(inf)
    names(results) <- c('Outliers - Inferiores')
    return(results)
  }
  else
  {
    results <- data.frame(sup)
    names(results) <- c('Outliers - Superiores')
    return(results)
  }
}

Assim, considerando a base de dados iris disponível no R, os outliers da variável Sepal.Width são obtidos por nossa função como:

f_outliers(iris$Sepal.Width, inferior = TRUE) # Outliers Inferiores
##   Outliers - Inferiores
## 1                     2
f_outliers(iris$Sepal.Width, inferior = FALSE) # Outliers Superiores
##   Outliers - Superiores
## 1                   4.4
## 2                   4.1
## 3                   4.2

3.4.2.3. Variância

A variância tem por objetivo mostrar o quão distante cada valor observado no conjunto de dados dados está da média. Ela pode ser calculada, matematicamente, pela seguinte expressão:

\[ S^2 = \dfrac{1}{n − 1} \sum_{i = 1}^n (x_i - \bar{x})^2\]

em que \(\bar{x}\) é a media amostral. No R, para calcular a variância, podemos trabalhar diretamente com a função var(). Assim, considerando a base de dados iris disponível no R, a variância da variável Sepal.Length é obtida pela rotina:

# Uso da função

# var(x, y = NULL, na.rm = FALSE, use)

var(iris$Sepal.Length)
## [1] 0.6856935

3.4.2.4. Desvio-Padrão

O desvio-padrão é uma medida de dispersão que indica o quanto um conjunto de dados é uniforme, isto é, quanto mais próximo de 0 for o desvio-padrão, mais homogêneo é o conjunto de dados. Tal medida é descrita por:

\[ S = \sqrt{\dfrac{1}{n − 1} \sum_{i = 1}^n (x_i - \bar{x})^2}\]

em que \(\bar{x}\) é a media amostral. No R, para calcular o desvio-padrão, podemos trabalhar diretamente com a função sd(). Assim, considerando a base de dados iris disponível no R, o desvio-padrão Sepal.Length é obtido pela rotina:

# Uso da função

# sd(x, na.rm = FALSE)

sd(iris$Sepal.Length) 
## [1] 0.8280661

3.4.2.5. Coeficiente de Variação

Por fim, a última medida de dispersão que temos é o coeficiente de variação (CV). O CV é conhecida por ser uma medida de dispersão pura (isto é, não possui unidade) utilizada, em geral, na comparação de grandezas com unidade de medida diferentes. Ele é calculado pela expressão:

\[ CV_\% = 100 \cdot \dfrac{s}{\bar{x}}\]

em que \(\bar{x}\) é a media amostral, e \(s\) é o desvio-padrão amostral. No R, não há uma função específica para o cálculo do CV, porém podemos trabalhar com as funções mean() e sd(). Assim, considerando a base de dados iris disponível no R, o CV Sepal.Length é obtido pela rotina:

# Uso das funções mean() e sd():

# mean(x, na.rm = FALSE, ...)
# sd(x, na.rm = FALSE)

# Cálculo do CV

CV <- 100 * (sd(iris$Sepal.Length) / mean(iris$Sepal.Length))
CV
## [1] 14.17113

3.4.3. Medidas de Associação

Para finalizar nosso estudo das medidas descritivas, nos resta entender os conceitos das medidas de associação que são utilizadas para descrever a relação entre duas ou mais variáveis. Dentre tais medidas, as principais são: covariância, e coeficiente de correlação.

3.4.3.1. Covariância

A primeira medida de associação que temos é a covariância. Esta medida é utilizada, em geral, para medir o grau de interdependência numérica entre duas variáveis de modo que se a covariância é igual à zero, então as variáveis são independentes. Matematicamente, ela é dada pela equação:

\[\text{Cov}(X,Y) = \dfrac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})\]

em que \(\bar{x}\) é a media amostral da variável \(X\), e em que \(\bar{y}\) é a media amostral da variável \(Y\). No R, para calcular a covariância, podemos trabalhar diretamente com a função cov(). Assim, considerando a base de dados iris disponível no R, a covariância entre as variáveis Sepal.Length e Petal.Length é obtida pela rotina:

# Uso da função

# cov(x, y = NULL, use = "everything",
#    method = c("pearson", "kendall", "spearman"))

cov(x = iris$Sepal.Length, y = iris$Petal.Length)
## [1] 1.274315

3.4.3.2. Coeficiente de Correlação

Uma vez definido o que é covariância, estamos aptos para definir o conceito de coeficiente de correlação de Pearson (r). Este coeficiente nada mais é que uma medida de associação que mede tanto a direção quanto a força de uma relação linear entre as variáveis X e Y. Matematicamente, esta medida é calculada pela equação:

\[r = \dfrac{\text{Cov}(X,Y)}{s_X s_Y}\]

em que \(s_X\) e \(s_Y\) são os desvios-padrões amostrais de ambas as variáveis.

Em geral, o que a correlação procura entender é como uma variável se comporta em um cenário onde outra está variando, visando identificar se existe alguma relação entre a variabilidade de ambas, e quantificando essa relação através de valores situados entre -1 e 1. Isto é, quando o \(r\) se aproxima de 1, nota-se um aumento no valor de uma variável quando a outra também aumenta (relação linear positiva). Por outro lado, quando o \(r\) se aproxima de -1, verifica-se que o valor de uma variável aumenta o da outra diminui (relação linear inversa).

No R, para calcular o coeficiente de correlação de Pearson, podemos trabalhar diretamente com a função cor(). Assim, considerando a base de dados iris disponível no R, o coeficiente de correlação entre as variáveis Sepal.Length e Petal.Length é obtida pela rotina:

# Uso da função cor():

# cor(x, y = NULL, use = "everything",
#    method = c("pearson", "kendall", "spearman"))

cor(x = iris$Sepal.Length, y = iris$Petal.Length, method = 'pearson')
## [1] 0.8717538

Existem vários métodos de correlação, no entanto, apenas três são implementados na função cor() do R: Pearson, Spearman, e Kendall. A correlação de Pearson, em geral, é a mais freqüentemente usada para variáveis quantitativas contínuas que possuem uma relação linear. Por outro lado, A correlação de Spearman (que na verdade é semelhante à de Pearson, mas baseada nos valores classificados para cada variável, e não nos dados brutos) é frequentemente usada para avaliar relacionamentos envolvendo pelo menos uma variável ordinal qualitativa ou duas variáveis quantitativas se a ligação for parcialmente linear. Por fim, a correlação de Kendall, que é calculado a partir do número de pares concordantes e discordantes, é frequentemente usado para variáveis ordinais qualitativas.

3.4.3.2.1. Matriz de Correlação

Suponha agora que queremos calcular correlações para vários pares de variáveis, em vez de um par de variáveis. Dentro do R, para realizar essa tarefa para todos os pares possíveis de variáveis no conjunto de dados, pode-se, novamente, trabalhar com a função cor(). Para exemplificar, considere o conjunto de dados iris disponível no R. Neste caso, para encontrar a matriz de correlação, seguimos a rotina:

## Carregar os dados

data(iris)

## Removendo a variável 'Species' por ser categórica

iris$Species <- NULL

## Matriz de Correlação:

# Uso da função cor():

# cor(x, y = NULL, use = "everything",
#    method = c("pearson", "kendall", "spearman"))

round(cor(iris), 2)
##              Sepal.Length Sepal.Width Petal.Length Petal.Width
## Sepal.Length         1.00       -0.12         0.87        0.82
## Sepal.Width         -0.12        1.00        -0.43       -0.37
## Petal.Length         0.87       -0.43         1.00        0.96
## Petal.Width          0.82       -0.37         0.96        1.00
3.4.3.2.2. Gráfico da Matriz de Correlação

Em algumas situações, a matriz de correlação apresentada anteriormente pode não ser facilmente interpretável, especialmente quando o conjunto de dados é composto por muitas variáveis. Então, para solucionar esse problema, trabalhamos com a visualização gráfica dessa matriz por meio do gráfico que chamamos de correlograma. No R, o correlograma pode ser construído a partir da função corrplot() do pacote corrplot. Para exemplificar, vamos, novamente, considerar a base de dados iris disponível no R.

## Carregar os dados

data(iris)

## Removendo a variável 'Species' por ser categórica

iris$Species <- NULL

## Carregar o pacote:

library(corrplot)
## Warning: package 'corrplot' was built under R version 4.2.2
## corrplot 0.92 loaded
## Gráfico da Matriz de Correlação:

# Uso da função corrplot():

# corrplot(corr,method = c("circle", "square", "ellipse", "number", "shade", "color", "pie"),
#         type = c("full", "lower", "upper"), col = NULL, col.lim = NULL, bg = "white", title = "",
#         is.corr = TRUE, add = FALSE, diag = TRUE, outline = FALSE, mar = c(0, 0, 0, 0), addgrid.col = NULL,
#         addCoef.col = NULL, addCoefasPercent = FALSE, order = c("original", "AOE", "FPC", "hclust", "alphabet"),
#         hclust.method = c("complete", "ward", "ward.D", "ward.D2", "single", "average", "mcquitty", "median", "centroid"),
#         addrect = NULL, rect.col = "black", rect.lwd = 2, tl.pos = NULL, tl.cex = 1, tl.col = "red", tl.offset = 0.4,
#         tl.srt = 90, cl.pos = NULL, cl.length = NULL, cl.cex = 0.8, cl.ratio = 0.15, cl.align.text = "c",
#         cl.offset = 0.5, number.cex = 1, number.font = 2, number.digits = NULL, addshade = c("negative", "positive", "all"),
#         shade.lwd = 1, shade.col = "white", p.mat = NULL, insig = c("pch", "p-value", "blank", "n", "label_sig"),
#         pch = 4, pch.col = "black", pch.cex = 3, plotCI = c("n", "square", "circle", "rect"), lowCI.mat = NULL,
#         uppCI.mat = NULL, na.label = "?", na.label.col = "black", win.asp = 1, sig.level = 0.05, ...)

corrplot(cor(iris),
  method = "number",
  type = "upper" # Mostra apenas a parte superior
)

Uma outra opção de trabalhar com essa visualização gráfica é utilizar a função ggcorrmat() do pacote ggstatsplot. Essa função mostra os coeficientes de correlação e, se houver, as correlações não-significativas (por padrão no nível de significância de 5% com o método de ajuste de Holm) são mostradas por uma grande cruz nos coeficientes de correlação. Para exemplificar, vamos, novamente, considerar a base de dados iris disponível no R.

## Carregar os dados

data(iris)

## Removendo a variável 'Species' por ser categórica

iris$Species <- NULL

## Carregar o pacote:

library(ggstatsplot)
## Warning: package 'ggstatsplot' was built under R version 4.2.2
## You can cite this package as:
##      Patil, I. (2021). Visualizations with statistical details: The 'ggstatsplot' approach.
##      Journal of Open Source Software, 6(61), 3167, doi:10.21105/joss.03167
## Gráfico da Matriz de Correlação:

# Uso da função corrplot():

# ggcorrmat(data, cor.vars = NULL, cor.vars.names = NULL, output = "plot", matrix.type = "upper",
#           type = "parametric", tr = 0.2, partial = FALSE, k = 2L, sig.level = 0.05, conf.level = 0.95,
#           bf.prior = 0.707, p.adjust.method = "holm", pch = "cross",
#           ggcorrplot.args = list(method = "square", outline.color = "black", pch.cex = 14),
#           package = "RColorBrewer", palette = "Dark2", colors = c("#E69F00", "white", "#009E73"),
#           ggtheme = ggstatsplot::theme_ggstatsplot(), ggplot.component = NULL, title = NULL,
#           subtitle = NULL, caption = NULL, ...)

ggcorrmat(
  data = iris,
  type = "parametric", # Argumento 'parametric' deve ser utilizado para a correlação de Pearson
  colors = c("darkred", "white", "steelblue") 
)

3.5. Tabelas de Contingência

As tabelas de contingência são formadas pelo cruzamento de duas variáveis categóricas, podendo ser de tamanho \(k \times n\), em que “k” representa o nº de linhas e “n” o de colunas. Tais tabelas podem ser aplicadas em diversas áreas, mas, frequentemente, são utilizadas no campo das ciências da saúde em estudos de testes de triagem, e no campo das ciências econômicas em estudos de modelos de crédito. No entanto, seu maior uso, ainda, é na aplicação de testes Qui-Quadrado.

No R, há várias formas de se construir uma tabela de contingência, sendo a mais usual pela função table(). Por exemplo, considere a base de dados referente aos dados de insuficiência cardíaca trabalhada anteriormente e, suponha, que nosso objetivo seja criar uma tabela de contingência referente ao sexo (variável Gender) e pressão alta (variável BP). Neste caso, podemos seguir a seguinte rotina em R:

## Leitura dos Dados (Dataset: Heart)

setwd('datasets')

db <- read.csv('heart.csv', header = TRUE, sep = ',')

## Reestruturação do Banco de Dados

db$Event    <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender   <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking  <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP       <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia  <- ifelse(db$Anaemia == 1, 'Yes', 'No')

## Visualização dos dados

head(db, n = 10)
##    Time        Event Gender Smoking Diabetes  BP Anaemia Age Ejection.Fraction
## 1    97     Censored Female      No       No  No     Yes  43                50
## 2   180     Censored   Male     Yes      Yes  No     Yes  73                30
## 3    31 Non-Censored   Male     Yes       No Yes      No  70                20
## 4    87     Censored   Male      No       No  No     Yes  65                25
## 5   113     Censored   Male      No       No  No      No  64                60
## 6    10 Non-Censored   Male      No       No  No     Yes  75                15
## 7   250     Censored   Male     Yes       No  No      No  70                40
## 8    27 Non-Censored   Male      No      Yes Yes      No  94                38
## 9    87     Censored   Male      No       No Yes      No  75                45
## 10   87     Censored   Male     Yes       No  No      No  80                25
##    Sodium Creatinine Pletelets  CPK
## 1     135       1.30    237000  358
## 2     142       1.18    160000  231
## 3     134       1.83    263358  582
## 4     141       1.10    298000  305
## 5     137       1.00    242000 1610
## 6     137       1.20    127000  246
## 7     136       2.70     51000  582
## 8     134       1.83    263358  582
## 9     137       1.18    263358  582
## 10    144       1.10    149000  898
## Tabela de Contingência 2x2: Sexo x Pressão Alta

# Uso da função table()
# table(..., exclude = if (useNA == "no") c(NA, NaN), 
#       useNA = c("no", "ifany", "always"),
#       dnn = list.names(...), deparse.level = 1)

table(db$Gender, db$BP)
##         
##           No Yes
##   Female  61  44
##   Male   133  61

A tabela de contigência, em geral, nos informa o número de “casos” em cada subgrupo. No entanto, em algumas situações, ao invés de trabalhar com as frequências absolutas (ou seja, o número de “casos”), pode-se trabalhar com as frequências relativas (ou seja, proporções) em cada subgrupo adicionando a função table() dentro da função prop.table(). Considerando o exemplo anterior, então, tem-se que:

## Tabela de Contingência 2x2: Sexo x Pressão Alta

table(db$Gender, db$BP)
##         
##           No Yes
##   Female  61  44
##   Male   133  61
## Tabela de Contingência 2x2: Sexo x Pressão Alta (Uso de Frequências Relativas)

# Uso da função prop.table()

# prop.table(x, margin = NULL)

prop.table(table(db$Gender, db$BP))
##         
##                 No       Yes
##   Female 0.2040134 0.1471572
##   Male   0.4448161 0.2040134

Observação: Podemos também pode calcular as porcentagens por linha ou por coluna adicionando um segundo argumento à função prop.table(): 1 para linha, ou 2 para coluna:

# Porcentagens por linha:
round(prop.table(table(db$Gender, db$BP), 1), 2) # arredonda para 2 dígitos com round()
##         
##            No  Yes
##   Female 0.58 0.42
##   Male   0.69 0.31
# Porcentagens por coluna:
round(prop.table(table(db$Gender, db$BP), 2), 2) # arredonda para 2 dígitos com round()
##         
##            No  Yes
##   Female 0.31 0.42
##   Male   0.69 0.58

Uma outra forma de trabalhar com as tabelas de contingência dentro do R é por meio da função ctable() do pacote summarytools. A função ctable() produz tabulações cruzadas (também conhecidas como tabelas de contingência) para pares de variáveis categóricas, utilizando frequências absolutas e também frequ~encias relativas (em percentual). Como exemplo, considere novamente as duas variáveis categóricas, sexo (variável Gender) e pressão alta (variável BP), de nosso conjunto de dados. Logo, tem-se que:

## Carregar pacote

library(summarytools)

## Tabela de Contingência 2x2: Sexo x Pressão Alta

# Uso da função ctable()

# ctable(x, y,prop = st_options("ctable.prop"),
#        useNA = "ifany", totals = st_options("ctable.totals"),
#        style = st_options("style"), round.digits = st_options("ctable.round.digits"),
#       justify = "right", plain.ascii = st_options("plain.ascii"),
#       headings = st_options("headings"), display.labels = st_options("display.labels"),
#       split.tables = Inf, dnn = c(substitute(x), substitute(y)),
#       chisq = FALSE, OR = FALSE, RR = FALSE, weights = NA, rescale.weights = FALSE,
#       ...)

ctable(x = db$Gender, y = db$BP)
## Cross-Tabulation, Row Proportions  
## Gender * BP  
## Data Frame: db  
## 
## -------- ---- ------------- ------------- --------------
##            BP            No           Yes          Total
##   Gender                                                
##   Female         61 (58.1%)    44 (41.9%)   105 (100.0%)
##     Male        133 (68.6%)    61 (31.4%)   194 (100.0%)
##    Total        194 (64.9%)   105 (35.1%)   299 (100.0%)
## -------- ---- ------------- ------------- --------------
## Tabela de Contingência 2x2: Sexo x Pressão Alta (Uso de Proporções Totais)
ctable(x = db$Gender, y = db$BP, prop = 't')
## Cross-Tabulation, Total Proportions  
## Gender * BP  
## Data Frame: db  
## 
## -------- ---- ------------- ------------- --------------
##            BP            No           Yes          Total
##   Gender                                                
##   Female         61 (20.4%)    44 (14.7%)   105 ( 35.1%)
##     Male        133 (44.5%)    61 (20.4%)   194 ( 64.9%)
##    Total        194 (64.9%)   105 (35.1%)   299 (100.0%)
## -------- ---- ------------- ------------- --------------

3.6. Resumos Descritivos

3.6.1. Pacote: summarytools

Um dos pacotes mais fundamentais para fazer um resumo descritivo é o pacote summarytools. Basicamente, este pacote é centrado em torno de 4 funções:

  • freq() para tabelas de frequências.
  • ctable() para tabulações cruzadas.
  • descr() para estatísticas descritivas.
  • dfSummary() para resumos de data.frame.

Para o nosso estudo, nesta seção, vamos considerar a função descr(). A função descr() produz estatísticas descritivas (univariadas) com as medidas de posição e de dispersão. Uma grande vantagem dessa função é que ela aceita vetores únicos, bem como data.frames. Se um data.frame for fornecido, todas as colunas não numéricas serão ignoradas para que você não precise removê-las antes de executar a função. Além disso a função descr() permite exibir:

  • Apenas uma seleção de estatísticas descritivas de sua escolha, com o argumento stats = c("mean", "sd") para média e desvio padrão.
  • Mínimo, primeiro quartil, mediana, terceiro quartil e máximo com stats = "fivenum".
  • As estatísticas descritivas mais comuns (média, desvio padrão, mínimo, mediano, máximo, número e porcentagem de observações válidas), com stats = "common".

Para exemplificar tais conceitos, considere a base de dados iris existente dentro software R. Neste caso, para cada uma das opções acima, trabalhamos com as rotinas:

# Carregar o Pacote

library(summarytools)

# Carregar os Dados

data(iris)

# Uso da funçã descr():

# descr(x, var = NULL, stats = st_options("descr.stats"), na.rm = TRUE, round.digits = st_options("round.digits"),
#       transpose = st_options("descr.transpose"), order = "sort", style = st_options("style"),
#       plain.ascii = st_options("plain.ascii"), justify = "r", headings = st_options("headings"),
#       display.labels = st_options("display.labels"), split.tables = 100, weights = NA, rescale.weights = FALSE,
#       ...)

# Resumo Descritivo: 'mean' e 'sd'

descr(iris, headings = FALSE, stats = c("mean", "sd"))
## Non-numerical variable(s) ignored: Species
## 
##                 Petal.Length   Petal.Width   Sepal.Length   Sepal.Width
## ------------- -------------- ------------- -------------- -------------
##          Mean           3.76          1.20           5.84          3.06
##       Std.Dev           1.77          0.76           0.83          0.44
# Resumo Descritivo: 'fivenum'

descr(iris, headings = FALSE, stats = "fivenum")
## Non-numerical variable(s) ignored: Species
## 
##                Petal.Length   Petal.Width   Sepal.Length   Sepal.Width
## ------------ -------------- ------------- -------------- -------------
##          Min           1.00          0.10           4.30          2.00
##           Q1           1.60          0.30           5.10          2.80
##       Median           4.35          1.30           5.80          3.00
##           Q3           5.10          1.80           6.40          3.30
##          Max           6.90          2.50           7.90          4.40
# Resumo Descritivo: 'common'

descr(iris, headings = FALSE, stats = "common")
## Non-numerical variable(s) ignored: Species
## 
##                   Petal.Length   Petal.Width   Sepal.Length   Sepal.Width
## --------------- -------------- ------------- -------------- -------------
##            Mean           3.76          1.20           5.84          3.06
##         Std.Dev           1.77          0.76           0.83          0.44
##             Min           1.00          0.10           4.30          2.00
##          Median           4.35          1.30           5.80          3.00
##             Max           6.90          2.50           7.90          4.40
##         N.Valid         150.00        150.00         150.00        150.00
##       Pct.Valid         100.00        100.00         100.00        100.00

Em algumas situações, podemos estar interessados em resumos descritivos por grupos. Neste caso, para calcular essas estatísticas descritivas por grupo, podemos, então, usar a função descr() em combinação com a função stby() (responsável para trabalhar com dados divididos em grupos) do pacote summarytools. Para exemplificar, consideremos a base de dados heart descrita anteriormente.

# Carregar o pacote

library(summarytools)

## Leitura dos Dados (Dataset: Heart)

setwd('datasets')

db <- read.csv('heart.csv', header = TRUE, sep = ',')

## Reestruturação do Banco de Dados

db$Event    <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender   <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking  <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP       <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia  <- ifelse(db$Anaemia == 1, 'Yes', 'No')

# Visualização dos dados

head(db, n = 10)
##    Time        Event Gender Smoking Diabetes  BP Anaemia Age Ejection.Fraction
## 1    97     Censored Female      No       No  No     Yes  43                50
## 2   180     Censored   Male     Yes      Yes  No     Yes  73                30
## 3    31 Non-Censored   Male     Yes       No Yes      No  70                20
## 4    87     Censored   Male      No       No  No     Yes  65                25
## 5   113     Censored   Male      No       No  No      No  64                60
## 6    10 Non-Censored   Male      No       No  No     Yes  75                15
## 7   250     Censored   Male     Yes       No  No      No  70                40
## 8    27 Non-Censored   Male      No      Yes Yes      No  94                38
## 9    87     Censored   Male      No       No Yes      No  75                45
## 10   87     Censored   Male     Yes       No  No      No  80                25
##    Sodium Creatinine Pletelets  CPK
## 1     135       1.30    237000  358
## 2     142       1.18    160000  231
## 3     134       1.83    263358  582
## 4     141       1.10    298000  305
## 5     137       1.00    242000 1610
## 6     137       1.20    127000  246
## 7     136       2.70     51000  582
## 8     134       1.83    263358  582
## 9     137       1.18    263358  582
## 10    144       1.10    149000  898
# Uso da função stby():

# stby(data, INDICES, FUN, ..., simplify = TRUE)

stby(data = db,
     INDICES = db$Gender, # por sexo
     FUN = descr, # resumo descritivo
     stats = "common",
     transpose = TRUE)
## Non-numerical variable(s) ignored: Event, Gender, Smoking, Diabetes, BP, Anaemia
## Descriptive Statistics  
## db  
## Group: Gender = Female  
## N: 105  
## 
##                                Mean     Std.Dev        Min      Median         Max   N.Valid   Pct.Valid
## ----------------------- ----------- ----------- ---------- ----------- ----------- --------- -----------
##                     Age       59.78       11.24      40.00       60.00       95.00    105.00      100.00
##                     CPK      476.78      611.36      52.00      250.00     3964.00    105.00      100.00
##              Creatinine        1.38        1.12       0.50        1.00        9.00    105.00      100.00
##       Ejection.Fraction       40.47       12.73      15.00       38.00       80.00    105.00      100.00
##               Pletelets   279964.02   102108.75   62000.00   263358.03   742000.00    105.00      100.00
##                  Sodium      136.79        4.90     116.00      137.00      146.00    105.00      100.00
##                    Time      131.90       77.63       8.00      109.00      278.00    105.00      100.00
## 
## Group: Gender = Male  
## N: 194  
## 
##                                Mean    Std.Dev        Min      Median         Max   N.Valid   Pct.Valid
## ----------------------- ----------- ---------- ---------- ----------- ----------- --------- -----------
##                     Age       61.41      12.22      40.00       60.00       95.00    194.00      100.00
##                     CPK      638.70    1114.89      23.00      249.00     7861.00    194.00      100.00
##              Creatinine        1.40       0.99       0.60        1.10        9.40    194.00      100.00
##       Ejection.Fraction       36.79      11.14      14.00       35.00       62.00    194.00      100.00
##               Pletelets   254370.25   94447.36   25100.00   253000.00   850000.00    194.00      100.00
##                  Sodium      136.54       4.13     113.00      137.00      148.00    194.00      100.00
##                    Time      129.37      77.79       4.00      117.50      285.00    194.00      100.00

Além das opções acima, dentro do pacote summarytools, pode ser que nosso interesse seja resumir dados contidos apenas na estrutura de data.frame(). Neste caso, pode-se trabalhar com a função dfSummary() do pacote summarytools. Para exemplificar, consideremos a base de dados iris disponível no R, e, também, a base de dados heart apresentada anteriormente.

# Carregar o Pacote

library(summarytools)

# Carregar os Dados

data(iris)

# Uso da função dfSummary():

# dfSummary(x, round.digits = 1, varnumbers = st_options("dfSummary.varnumbers"),
#           labels.col = st_options("dfSummary.labels.col"),
#           valid.col = st_options("dfSummary.valid.col"),
#           na.col = st_options("dfSummary.na.col"),
#           graph.col = st_options("dfSummary.graph.col"),
#           graph.magnif = st_options("dfSummary.graph.magnif"),
#           style = st_options("dfSummary.style"),
#           plain.ascii = st_options("plain.ascii"),
#           justify = "l", col.widths = NA, headings = st_options("headings"),
#           display.labels = st_options("display.labels"),
#           max.distinct.values = 10, trim.strings = FALSE,
#           max.string.width = 25, split.cells = 40,
#           split.tables = Inf, tmp.img.dir = st_options("tmp.img.dir"),
#           keep.grp.vars = FALSE, silent = st_options("dfSummary.silent"),
#           ...)

dfSummary(iris)
## Data Frame Summary  
## iris  
## Dimensions: 150 x 5  
## Duplicates: 1  
## 
## -----------------------------------------------------------------------------------------------------------
## No   Variable       Stats / Values          Freqs (% of Valid)   Graph                 Valid      Missing  
## ---- -------------- ----------------------- -------------------- --------------------- ---------- ---------
## 1    Sepal.Length   Mean (sd) : 5.8 (0.8)   35 distinct values     . . : :             150        0        
##      [numeric]      min < med < max:                               : : : :             (100.0%)   (0.0%)   
##                     4.3 < 5.8 < 7.9                                : : : : :                               
##                     IQR (CV) : 1.3 (0.1)                           : : : : :                               
##                                                                  : : : : : : : :                           
## 
## 2    Sepal.Width    Mean (sd) : 3.1 (0.4)   23 distinct values           :             150        0        
##      [numeric]      min < med < max:                                     :             (100.0%)   (0.0%)   
##                     2 < 3 < 4.4                                        . :                                 
##                     IQR (CV) : 0.5 (0.1)                             : : : :                               
##                                                                  . . : : : : : :                           
## 
## 3    Petal.Length   Mean (sd) : 3.8 (1.8)   43 distinct values   :                     150        0        
##      [numeric]      min < med < max:                             :         . :         (100.0%)   (0.0%)   
##                     1 < 4.3 < 6.9                                :         : : .                           
##                     IQR (CV) : 3.5 (0.5)                         : :       : : : .                         
##                                                                  : :   . : : : : : .                       
## 
## 4    Petal.Width    Mean (sd) : 1.2 (0.8)   22 distinct values   :                     150        0        
##      [numeric]      min < med < max:                             :                     (100.0%)   (0.0%)   
##                     0.1 < 1.3 < 2.5                              :       . .   :                           
##                     IQR (CV) : 1.5 (0.6)                         :       : :   :   .                       
##                                                                  : :   : : : . : : :                       
## 
## 5    Species        1. setosa               50 (33.3%)           IIIIII                150        0        
##      [factor]       2. versicolor           50 (33.3%)           IIIIII                (100.0%)   (0.0%)   
##                     3. virginica            50 (33.3%)           IIIIII                                    
## -----------------------------------------------------------------------------------------------------------
# Carregar o pacote

library(summarytools)

## Leitura dos Dados (Dataset: Heart)

setwd('datasets')

db <- read.csv('heart.csv', header = TRUE, sep = ',')

## Reestruturação do Banco de Dados

db$Event    <- ifelse(db$Event == 1, 'Non-Censored', 'Censored')
db$Gender   <- ifelse(db$Gender == 1, 'Male', 'Female')
db$Smoking  <- ifelse(db$Smoking == 1, 'Yes', 'No')
db$Diabetes <- ifelse(db$Diabetes == 1, 'Yes', 'No')
db$BP       <- ifelse(db$BP == 1, 'Yes', 'No')
db$Anaemia  <- ifelse(db$Anaemia == 1, 'Yes', 'No')

# Visualização dos dados

head(db, n = 10)
##    Time        Event Gender Smoking Diabetes  BP Anaemia Age Ejection.Fraction
## 1    97     Censored Female      No       No  No     Yes  43                50
## 2   180     Censored   Male     Yes      Yes  No     Yes  73                30
## 3    31 Non-Censored   Male     Yes       No Yes      No  70                20
## 4    87     Censored   Male      No       No  No     Yes  65                25
## 5   113     Censored   Male      No       No  No      No  64                60
## 6    10 Non-Censored   Male      No       No  No     Yes  75                15
## 7   250     Censored   Male     Yes       No  No      No  70                40
## 8    27 Non-Censored   Male      No      Yes Yes      No  94                38
## 9    87     Censored   Male      No       No Yes      No  75                45
## 10   87     Censored   Male     Yes       No  No      No  80                25
##    Sodium Creatinine Pletelets  CPK
## 1     135       1.30    237000  358
## 2     142       1.18    160000  231
## 3     134       1.83    263358  582
## 4     141       1.10    298000  305
## 5     137       1.00    242000 1610
## 6     137       1.20    127000  246
## 7     136       2.70     51000  582
## 8     134       1.83    263358  582
## 9     137       1.18    263358  582
## 10    144       1.10    149000  898
# Uso da função dfSummary():

# dfSummary(x, round.digits = 1, varnumbers = st_options("dfSummary.varnumbers"),
#           labels.col = st_options("dfSummary.labels.col"),
#           valid.col = st_options("dfSummary.valid.col"),
#           na.col = st_options("dfSummary.na.col"),
#           graph.col = st_options("dfSummary.graph.col"),
#           graph.magnif = st_options("dfSummary.graph.magnif"),
#           style = st_options("dfSummary.style"),
#           plain.ascii = st_options("plain.ascii"),
#           justify = "l", col.widths = NA, headings = st_options("headings"),
#           display.labels = st_options("display.labels"),
#           max.distinct.values = 10, trim.strings = FALSE,
#           max.string.width = 25, split.cells = 40,
#           split.tables = Inf, tmp.img.dir = st_options("tmp.img.dir"),
#           keep.grp.vars = FALSE, silent = st_options("dfSummary.silent"),
#           ...)

dfSummary(db)
## Data Frame Summary  
## db  
## Dimensions: 299 x 13  
## Duplicates: 0  
## 
## ------------------------------------------------------------------------------------------------------------------------
## No   Variable            Stats / Values                 Freqs (% of Valid)    Graph                 Valid      Missing  
## ---- ------------------- ------------------------------ --------------------- --------------------- ---------- ---------
## 1    Time                Mean (sd) : 130.3 (77.6)       148 distinct values     :     .             299        0        
##      [integer]           min < med < max:                                     : : :   :             (100.0%)   (0.0%)   
##                          4 < 115 < 285                                        : : : : :                                 
##                          IQR (CV) : 130 (0.6)                                 : : : : :                                 
##                                                                               : : : : : :                               
## 
## 2    Event               1. Censored                    203 (67.9%)           IIIIIIIIIIIII         299        0        
##      [character]         2. Non-Censored                 96 (32.1%)           IIIIII                (100.0%)   (0.0%)   
## 
## 3    Gender              1. Female                      105 (35.1%)           IIIIIII               299        0        
##      [character]         2. Male                        194 (64.9%)           IIIIIIIIIIII          (100.0%)   (0.0%)   
## 
## 4    Smoking             1. No                          203 (67.9%)           IIIIIIIIIIIII         299        0        
##      [character]         2. Yes                          96 (32.1%)           IIIIII                (100.0%)   (0.0%)   
## 
## 5    Diabetes            1. No                          174 (58.2%)           IIIIIIIIIII           299        0        
##      [character]         2. Yes                         125 (41.8%)           IIIIIIII              (100.0%)   (0.0%)   
## 
## 6    BP                  1. No                          194 (64.9%)           IIIIIIIIIIII          299        0        
##      [character]         2. Yes                         105 (35.1%)           IIIIIII               (100.0%)   (0.0%)   
## 
## 7    Anaemia             1. No                          170 (56.9%)           IIIIIIIIIII           299        0        
##      [character]         2. Yes                         129 (43.1%)           IIIIIIII              (100.0%)   (0.0%)   
## 
## 8    Age                 Mean (sd) : 60.8 (11.9)        47 distinct values          :               299        0        
##      [numeric]           min < med < max:                                       .   : . .           (100.0%)   (0.0%)   
##                          40 < 60 < 95                                         : : : : : :                               
##                          IQR (CV) : 19 (0.2)                                  : : : : : :                               
##                                                                               : : : : : : : : . .                       
## 
## 9    Ejection.Fraction   Mean (sd) : 38.1 (11.8)        17 distinct values          :               299        0        
##      [integer]           min < med < max:                                           :               (100.0%)   (0.0%)   
##                          14 < 38 < 80                                               :                                   
##                          IQR (CV) : 15 (0.3)                                    . . :     .                             
##                                                                               : : : : : : :                             
## 
## 10   Sodium              Mean (sd) : 136.6 (4.4)        27 distinct values              :           299        0        
##      [integer]           min < med < max:                                               :           (100.0%)   (0.0%)   
##                          113 < 137 < 148                                              . :                               
##                          IQR (CV) : 6 (0)                                             : : .                             
##                                                                                     . : : :                             
## 
## 11   Creatinine          Mean (sd) : 1.4 (1)            40 distinct values    :                     299        0        
##      [numeric]           min < med < max:                                     :                     (100.0%)   (0.0%)   
##                          0.5 < 1.1 < 9.4                                      :                                         
##                          IQR (CV) : 0.5 (0.7)                                 :                                         
##                                                                               : : .                                     
## 
## 12   Pletelets           Mean (sd) : 263358 (97804.2)   176 distinct values       :                 299        0        
##      [numeric]           min < med < max:                                         :                 (100.0%)   (0.0%)   
##                          25100 < 262000 < 850000                                  :                                     
##                          IQR (CV) : 91000 (0.4)                                 : : :                                   
##                                                                               . : : : .                                 
## 
## 13   CPK                 Mean (sd) : 581.8 (970.3)      208 distinct values   :                     299        0        
##      [integer]           min < med < max:                                     :                     (100.0%)   (0.0%)   
##                          23 < 250 < 7861                                      :                                         
##                          IQR (CV) : 465.5 (1.7)                               :                                         
##                                                                               : .                                       
## ------------------------------------------------------------------------------------------------------------------------

3.6.2. Pacote: psych

Um outro pacote que é muito útil para fazer um resumo descritivo é o pacote psych. Neste pacote, a função describeBy() deste pacote nos permite relatar um grande rol de medidas descritivas como, por exemplo, número de observações válidas, média, desvio-padrão, mediana, desvio absoluto (em relação a mediana), assimetria, curtose, entre outras. Além disso, também permite o uso de variáveis de grupo para exibir o resumo descritivo. Para exemplificar, vamos considerar a base de dados iris disponível no R.

# Carregar o Pacote

library(psych)
## 
## Attaching package: 'psych'
## The following objects are masked from 'package:ggplot2':
## 
##     %+%, alpha
## The following objects are masked from 'package:epiDisplay':
## 
##     alpha, cs, lookup
# Carregar os Dados

data(iris)

# Uso da função describeBy():

describeBy(iris, iris$Species)
## 
##  Descriptive statistics by group 
## group: setosa
##              vars  n mean   sd median trimmed  mad min max range skew kurtosis
## Sepal.Length    1 50 5.01 0.35    5.0    5.00 0.30 4.3 5.8   1.5 0.11    -0.45
## Sepal.Width     2 50 3.43 0.38    3.4    3.42 0.37 2.3 4.4   2.1 0.04     0.60
## Petal.Length    3 50 1.46 0.17    1.5    1.46 0.15 1.0 1.9   0.9 0.10     0.65
## Petal.Width     4 50 0.25 0.11    0.2    0.24 0.00 0.1 0.6   0.5 1.18     1.26
## Species*        5 50 1.00 0.00    1.0    1.00 0.00 1.0 1.0   0.0  NaN      NaN
##                se
## Sepal.Length 0.05
## Sepal.Width  0.05
## Petal.Length 0.02
## Petal.Width  0.01
## Species*     0.00
## ------------------------------------------------------------ 
## group: versicolor
##              vars  n mean   sd median trimmed  mad min max range  skew kurtosis
## Sepal.Length    1 50 5.94 0.52   5.90    5.94 0.52 4.9 7.0   2.1  0.10    -0.69
## Sepal.Width     2 50 2.77 0.31   2.80    2.78 0.30 2.0 3.4   1.4 -0.34    -0.55
## Petal.Length    3 50 4.26 0.47   4.35    4.29 0.52 3.0 5.1   2.1 -0.57    -0.19
## Petal.Width     4 50 1.33 0.20   1.30    1.32 0.22 1.0 1.8   0.8 -0.03    -0.59
## Species*        5 50 2.00 0.00   2.00    2.00 0.00 2.0 2.0   0.0   NaN      NaN
##                se
## Sepal.Length 0.07
## Sepal.Width  0.04
## Petal.Length 0.07
## Petal.Width  0.03
## Species*     0.00
## ------------------------------------------------------------ 
## group: virginica
##              vars  n mean   sd median trimmed  mad min max range  skew kurtosis
## Sepal.Length    1 50 6.59 0.64   6.50    6.57 0.59 4.9 7.9   3.0  0.11    -0.20
## Sepal.Width     2 50 2.97 0.32   3.00    2.96 0.30 2.2 3.8   1.6  0.34     0.38
## Petal.Length    3 50 5.55 0.55   5.55    5.51 0.67 4.5 6.9   2.4  0.52    -0.37
## Petal.Width     4 50 2.03 0.27   2.00    2.03 0.30 1.4 2.5   1.1 -0.12    -0.75
## Species*        5 50 3.00 0.00   3.00    3.00 0.00 3.0 3.0   0.0   NaN      NaN
##                se
## Sepal.Length 0.09
## Sepal.Width  0.05
## Petal.Length 0.08
## Petal.Width  0.04
## Species*     0.00

3.7. O Uso do tidyverse

O tidyverse é um conjunto de pacotes que tornam o R mais fácil de usar. Todos os pacotes trabalham juntos e compartilham uma gramática e filosofia subjacentes. Isso mesmo - filosofia. O tidyverse opera na suposição de que os dados devem ser “arrumados”. De acordo com Hadley Wickham, cientista-chefe do RStudio e um dos criadores do tidyverse:

Dados organizados são uma maneira padrão de mapear o significado de um conjunto de dados para sua estrutura. Um conjunto de dados é classificado como confuso ou organizado dependendo de como as linhas, colunas e tabelas são combinadas com as observações e as variáveis.

Em geral, os dados são ditos organizados se:

  • Cada variável forma uma coluna.
  • Cada observação forma uma linha.
  • Cada tipo de unidade observacional forma uma tabela.

Observação: O tidyverse não apenas ajuda a manter os dados “arrumados”, mas torna a programação mais fácil em comparação com a sintaxe básica do R.

3.7.1. Extraindo/Criando Variáveis

Para iniciar nossos estudos sobre o tidyverse, consideremos a base de dados iris disponível no R. A partir dessa base de dados, iniciemos, então, com a seleção/extração de uma variável no tidyverse de acordo com a seguinte rotina em R:

## Carregar o pacote

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.2.2
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ tibble  3.1.7     ✔ purrr   0.3.4
## ✔ tidyr   1.2.0     ✔ stringr 1.4.0
## ✔ readr   2.1.2     ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ psych::%+%()     masks ggplot2::%+%()
## ✖ psych::alpha()   masks ggplot2::alpha(), epiDisplay::alpha()
## ✖ plotly::filter() masks dplyr::filter(), stats::filter()
## ✖ dplyr::first()   masks xts::first()
## ✖ dplyr::lag()     masks stats::lag()
## ✖ dplyr::last()    masks xts::last()
## ✖ plotly::select() masks dplyr::select(), MASS::select()
## ✖ tibble::view()   masks summarytools::view()
## Carregar os dados

data(iris)

## Seleção/Extração de variáveis

# Uso da função select():

# select(.data, ...) # Para ler mais sobre a função, usar help(select)

select(iris, Species, Petal.Width) # por nome
##        Species Petal.Width
## 1       setosa         0.2
## 2       setosa         0.2
## 3       setosa         0.2
## 4       setosa         0.2
## 5       setosa         0.2
## 6       setosa         0.4
## 7       setosa         0.3
## 8       setosa         0.2
## 9       setosa         0.2
## 10      setosa         0.1
## 11      setosa         0.2
## 12      setosa         0.2
## 13      setosa         0.1
## 14      setosa         0.1
## 15      setosa         0.2
## 16      setosa         0.4
## 17      setosa         0.4
## 18      setosa         0.3
## 19      setosa         0.3
## 20      setosa         0.3
## 21      setosa         0.2
## 22      setosa         0.4
## 23      setosa         0.2
## 24      setosa         0.5
## 25      setosa         0.2
## 26      setosa         0.2
## 27      setosa         0.4
## 28      setosa         0.2
## 29      setosa         0.2
## 30      setosa         0.2
## 31      setosa         0.2
## 32      setosa         0.4
## 33      setosa         0.1
## 34      setosa         0.2
## 35      setosa         0.2
## 36      setosa         0.2
## 37      setosa         0.2
## 38      setosa         0.1
## 39      setosa         0.2
## 40      setosa         0.2
## 41      setosa         0.3
## 42      setosa         0.3
## 43      setosa         0.2
## 44      setosa         0.6
## 45      setosa         0.4
## 46      setosa         0.3
## 47      setosa         0.2
## 48      setosa         0.2
## 49      setosa         0.2
## 50      setosa         0.2
## 51  versicolor         1.4
## 52  versicolor         1.5
## 53  versicolor         1.5
## 54  versicolor         1.3
## 55  versicolor         1.5
## 56  versicolor         1.3
## 57  versicolor         1.6
## 58  versicolor         1.0
## 59  versicolor         1.3
## 60  versicolor         1.4
## 61  versicolor         1.0
## 62  versicolor         1.5
## 63  versicolor         1.0
## 64  versicolor         1.4
## 65  versicolor         1.3
## 66  versicolor         1.4
## 67  versicolor         1.5
## 68  versicolor         1.0
## 69  versicolor         1.5
## 70  versicolor         1.1
## 71  versicolor         1.8
## 72  versicolor         1.3
## 73  versicolor         1.5
## 74  versicolor         1.2
## 75  versicolor         1.3
## 76  versicolor         1.4
## 77  versicolor         1.4
## 78  versicolor         1.7
## 79  versicolor         1.5
## 80  versicolor         1.0
## 81  versicolor         1.1
## 82  versicolor         1.0
## 83  versicolor         1.2
## 84  versicolor         1.6
## 85  versicolor         1.5
## 86  versicolor         1.6
## 87  versicolor         1.5
## 88  versicolor         1.3
## 89  versicolor         1.3
## 90  versicolor         1.3
## 91  versicolor         1.2
## 92  versicolor         1.4
## 93  versicolor         1.2
## 94  versicolor         1.0
## 95  versicolor         1.3
## 96  versicolor         1.2
## 97  versicolor         1.3
## 98  versicolor         1.3
## 99  versicolor         1.1
## 100 versicolor         1.3
## 101  virginica         2.5
## 102  virginica         1.9
## 103  virginica         2.1
## 104  virginica         1.8
## 105  virginica         2.2
## 106  virginica         2.1
## 107  virginica         1.7
## 108  virginica         1.8
## 109  virginica         1.8
## 110  virginica         2.5
## 111  virginica         2.0
## 112  virginica         1.9
## 113  virginica         2.1
## 114  virginica         2.0
## 115  virginica         2.4
## 116  virginica         2.3
## 117  virginica         1.8
## 118  virginica         2.2
## 119  virginica         2.3
## 120  virginica         1.5
## 121  virginica         2.3
## 122  virginica         2.0
## 123  virginica         2.0
## 124  virginica         1.8
## 125  virginica         2.1
## 126  virginica         1.8
## 127  virginica         1.8
## 128  virginica         1.8
## 129  virginica         2.1
## 130  virginica         1.6
## 131  virginica         1.9
## 132  virginica         2.0
## 133  virginica         2.2
## 134  virginica         1.5
## 135  virginica         1.4
## 136  virginica         2.3
## 137  virginica         2.4
## 138  virginica         1.8
## 139  virginica         1.8
## 140  virginica         2.1
## 141  virginica         2.4
## 142  virginica         2.3
## 143  virginica         1.9
## 144  virginica         2.3
## 145  virginica         2.5
## 146  virginica         2.3
## 147  virginica         1.9
## 148  virginica         2.0
## 149  virginica         2.3
## 150  virginica         1.8
select(iris, 5, 4)  # pelo índice da coluna
##        Species Petal.Width
## 1       setosa         0.2
## 2       setosa         0.2
## 3       setosa         0.2
## 4       setosa         0.2
## 5       setosa         0.2
## 6       setosa         0.4
## 7       setosa         0.3
## 8       setosa         0.2
## 9       setosa         0.2
## 10      setosa         0.1
## 11      setosa         0.2
## 12      setosa         0.2
## 13      setosa         0.1
## 14      setosa         0.1
## 15      setosa         0.2
## 16      setosa         0.4
## 17      setosa         0.4
## 18      setosa         0.3
## 19      setosa         0.3
## 20      setosa         0.3
## 21      setosa         0.2
## 22      setosa         0.4
## 23      setosa         0.2
## 24      setosa         0.5
## 25      setosa         0.2
## 26      setosa         0.2
## 27      setosa         0.4
## 28      setosa         0.2
## 29      setosa         0.2
## 30      setosa         0.2
## 31      setosa         0.2
## 32      setosa         0.4
## 33      setosa         0.1
## 34      setosa         0.2
## 35      setosa         0.2
## 36      setosa         0.2
## 37      setosa         0.2
## 38      setosa         0.1
## 39      setosa         0.2
## 40      setosa         0.2
## 41      setosa         0.3
## 42      setosa         0.3
## 43      setosa         0.2
## 44      setosa         0.6
## 45      setosa         0.4
## 46      setosa         0.3
## 47      setosa         0.2
## 48      setosa         0.2
## 49      setosa         0.2
## 50      setosa         0.2
## 51  versicolor         1.4
## 52  versicolor         1.5
## 53  versicolor         1.5
## 54  versicolor         1.3
## 55  versicolor         1.5
## 56  versicolor         1.3
## 57  versicolor         1.6
## 58  versicolor         1.0
## 59  versicolor         1.3
## 60  versicolor         1.4
## 61  versicolor         1.0
## 62  versicolor         1.5
## 63  versicolor         1.0
## 64  versicolor         1.4
## 65  versicolor         1.3
## 66  versicolor         1.4
## 67  versicolor         1.5
## 68  versicolor         1.0
## 69  versicolor         1.5
## 70  versicolor         1.1
## 71  versicolor         1.8
## 72  versicolor         1.3
## 73  versicolor         1.5
## 74  versicolor         1.2
## 75  versicolor         1.3
## 76  versicolor         1.4
## 77  versicolor         1.4
## 78  versicolor         1.7
## 79  versicolor         1.5
## 80  versicolor         1.0
## 81  versicolor         1.1
## 82  versicolor         1.0
## 83  versicolor         1.2
## 84  versicolor         1.6
## 85  versicolor         1.5
## 86  versicolor         1.6
## 87  versicolor         1.5
## 88  versicolor         1.3
## 89  versicolor         1.3
## 90  versicolor         1.3
## 91  versicolor         1.2
## 92  versicolor         1.4
## 93  versicolor         1.2
## 94  versicolor         1.0
## 95  versicolor         1.3
## 96  versicolor         1.2
## 97  versicolor         1.3
## 98  versicolor         1.3
## 99  versicolor         1.1
## 100 versicolor         1.3
## 101  virginica         2.5
## 102  virginica         1.9
## 103  virginica         2.1
## 104  virginica         1.8
## 105  virginica         2.2
## 106  virginica         2.1
## 107  virginica         1.7
## 108  virginica         1.8
## 109  virginica         1.8
## 110  virginica         2.5
## 111  virginica         2.0
## 112  virginica         1.9
## 113  virginica         2.1
## 114  virginica         2.0
## 115  virginica         2.4
## 116  virginica         2.3
## 117  virginica         1.8
## 118  virginica         2.2
## 119  virginica         2.3
## 120  virginica         1.5
## 121  virginica         2.3
## 122  virginica         2.0
## 123  virginica         2.0
## 124  virginica         1.8
## 125  virginica         2.1
## 126  virginica         1.8
## 127  virginica         1.8
## 128  virginica         1.8
## 129  virginica         2.1
## 130  virginica         1.6
## 131  virginica         1.9
## 132  virginica         2.0
## 133  virginica         2.2
## 134  virginica         1.5
## 135  virginica         1.4
## 136  virginica         2.3
## 137  virginica         2.4
## 138  virginica         1.8
## 139  virginica         1.8
## 140  virginica         2.1
## 141  virginica         2.4
## 142  virginica         2.3
## 143  virginica         1.9
## 144  virginica         2.3
## 145  virginica         2.5
## 146  virginica         2.3
## 147  virginica         1.9
## 148  virginica         2.0
## 149  virginica         2.3
## 150  virginica         1.8

Por outro lado, pode-se também trabalhar com a criação de novas colunas. Neste caso, considerando a base de dados iris, vamos criar as colunas Petal.Ratio e Sepal.Ratio por meio da função mutate() do tidyverse. Isto é,

## Carregar os dados

data(iris)

## Criação de novas variáveis

# Uso da função mutate():

# mutate(.data, ..., .keep = c("all", "used", "unused", "none"),
#        .before = NULL, .after = NULL)

iris <- iris %>%
             mutate(Petal.Ratio = Petal.Length/Petal.Width,
                    Sepal.Ratio = Sepal.Length/Sepal.Width)

head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species Petal.Ratio
## 1          5.1         3.5          1.4         0.2  setosa        7.00
## 2          4.9         3.0          1.4         0.2  setosa        7.00
## 3          4.7         3.2          1.3         0.2  setosa        6.50
## 4          4.6         3.1          1.5         0.2  setosa        7.50
## 5          5.0         3.6          1.4         0.2  setosa        7.00
## 6          5.4         3.9          1.7         0.4  setosa        4.25
##   Sepal.Ratio
## 1    1.457143
## 2    1.633333
## 3    1.468750
## 4    1.483871
## 5    1.388889
## 6    1.384615

3.7.2. Estatísticas Descritivas: Função summarize()

No tidyverse, para trabalhar com as estatísticas descritivas, podemos utilizar a função summarize() aninhada com a função group_by(). A função summarize() é muito semelhante a função mutate(), porém, em vez de anexar uma coluna ao banco de dados, ela resume e condensa todos os dados no número de colunas especificado. Para exemplificar o funcionamento dessa função, vamos criar um resumo descritivo da variável Petal.Width agrupado por espécie (variável Species) pertencente à base de dados iris:

iris %>%
  group_by(Species) %>%
  summarize(media = mean(Petal.Width),
            mediana = median(Petal.Width),
            desvio_padrao = sd(Petal.Width),
            tamanho_amostra = n())
## # A tibble: 3 × 5
##   Species    media mediana desvio_padrao tamanho_amostra
##   <fct>      <dbl>   <dbl>         <dbl>           <int>
## 1 setosa     0.246     0.2         0.105              50
## 2 versicolor 1.33      1.3         0.198              50
## 3 virginica  2.03      2           0.275              50

3.8. Outras Aplicações

3.8.1. Índices de Poluição do Ar

O arquivo zip para esta atividade é “specdata.zip”. O arquivo .zip contém 332 arquivos de valores separados por vírgula (CSV) contendo dados de monitoramento de poluição para poluição do ar com base no material particulado (PM) em 332 locais nos Estados Unidos. Cada arquivo contém dados de um única estação de monitoramento e o ID de cada estação está contido no nome do arquivo. Por exemplo, os dados da estação 200 estão contidos no arquivo “200.csv”. Cada arquivo contém três variáveis:

  • Date: a data da observação no formato AAAA-MM-DD (ano-mês-dia)
  • sulfate: o nível de sulfato (PM) no ar naquela data (medido em microgramas por metro cúbico)
  • nitrate: o nível de nitrato (PM) no ar naquela data (medido em microgramas por metro cúbico)

Para esta tarefa, você precisará descompactar este arquivo e criar o diretório ‘specdata’. Depois de descompactar o arquivo zip, não faça nenhuma modificação nos arquivos no diretório ‘specdata’. Em cada arquivo, você notará que há muitos dias em que sulfato ou nitrato (ou ambos) estão ausentes (codificados como ‘NA’). Isso é comum com dados de monitoramento de poluição do ar nos Estados Unidos.

3.8.1.1. Função pollutantmean

Escreva uma função chamada ‘pollutantmean’ que calcula a média de um poluente (sulfato ou nitrato) em uma lista específica de estaões de monitoramento. A função ‘pollutantmean’ tem três argumentos: ‘directory’, ‘pollutant’, e ‘id’. Dado um vetor com os números de ID, ‘pollutantmean’ lê os dados de material particulado das estações do diretório especificado no argumento ‘directory’ e retorna a média do poluente em todas as estações de monitorimento, ignorando quaisquer valores ausentes codificados como ‘NA’. Um protótipo da função é o seguinte:

pollutantmean    <- function(directory, pollutant, id = 1:332) 
{
    ## 'directory' é um vetor de caracteres de comprimento 1 que indica a localização dos arquivos CSV.

    ## 'pollutant' é um vetor de caracteres de comprimento 1 indicando o nome do poluente para o qual 
    ## calcularemos a média; ou "sulfato" ou "nitrato".

    ## 'id' é um vetor inteiro que indica os números de ID das estações a serem usadas.

    ## Retornar a média do poluente em todas as estações da lista no vetor 'id' (ignorando os valores NA).
    
    ## NOTA: Não arredondar o resultado!
}

3.8.1.2 Função complete

Escreva uma função que leia um diretório cheio de arquivos e relate o número de casos completamente observados em cada arquivo de dados. A função deve retornar um data frame onde a primeira coluna é o nome do arquivo e a segunda coluna é o número de casos completos. Segue um protótipo desta função:

complete         <- function(directory, id = 1:332) 
{
    ## 'directory' é um vetor de caracteres de comprimento 1 que indica a localização dos arquivos CSV.

    ## 'id' é um vetor inteiro que indica os números de ID das estações a serem usadas.
            
    ## Retorne um data.frame da seguinte forma:
    ## id nobs
    ## 1  117
    ## 2  1041
    ## ...
    ## onde 'id' é o ID da estação e 'nobs' é o número de casos completos.
}

3.8.1.3. Função corr

Escreva uma função que pegue um diretório de arquivos de dados e um limite (threshold) para casos completos e calcule a correlação entre sulfato e nitrato para locais de monitoramento onde o número de casos completamente observados (em todas as variáveis) é maior que o limite (threshold). A função deve retornar um vetor de correlações para as estações que atendem ao requisito do limite (threshold). Se nenhuma estação atender ao requisito de limite (threshold), a função deve retornar um vetor numérico de comprimento 0. Segue um protótipo dessa função:

corr             <- function(directory, threshold = 0) 
{
    ## 'directory' é um vetor de caracteres de comprimento 1 que indica a localização dos arquivos CSV.

    ## 'threshold' é um vetor numérico de comprimento 1 que indica o número de observações completamente observadas 
    ## (em todas as variáveis) necessárias para calcular a correlação entre nitrato e sulfato; o padrão é 0.

    ## Retorne um vetor numérico com as correlações.
    ## NOTA: Não arredondar o resultado!
}

3.8.2. Rank de Hospitais

Os dados para esta tarefa vêm do site Hospital Compare (http://hospitalcompare.hhs.gov) administrado pelo Departamento de Saúde e Serviços Humanos dos EUA. O objetivo do site é fornecer dados e informações sobre a qualidade do atendimento em mais de 4.000 hospitais certificados pelo Medicare nos EUA. Esse conjunto de dados cobre essencialmente todos os principais hospitais dos EUA, sendo utilizado para diversas finalidades, incluindo determinar se os hospitais devem ser multados por não fornecerem atendimento de alta qualidade aos pacientes (consulte http://goo.gl/jAXFX para obter informações sobre esse tópico específico).

O site do Hospital Compare contém muitos dados e veremos apenas um pequeno subconjunto para esta tarefa. O arquivo zip para esta tarefa contém dois arquivos:

  • result-of-care-measures.csv: Contém informações sobre mortalidade em 30 dias e taxas de readmissão por ataques cardíacos, insuficiência cardíaca e pneumonia para mais de 4.000 hospitais.
  • hospital-data.csv: Contém informações sobre cada hospital.

Para esta tarefa, você precisará descompactar este arquivo e criar o diretório ‘hospitaldata’. Depois de descompactar o arquivo zip, não faça nenhuma modificação nos arquivos no diretório ‘hospitaldata’.

3.8.2.1. Função best

Escreva uma função chamada ‘best’ que receba dois argumentos: o nome do estado abreviado em 2 caracteres e o nome do resultado. A função lê o arquivo outcome-of-care-measures.csv e retorna um vetor de caracteres com o nome do hospital que tem a melhor (ou seja, a menor) mortalidade em 30 dias para o resultado especificado naquele estado. O nome do hospital é o nome fornecido na variável Hospital.Name. Os resultados podem ser “heart attack”, “heart failure” ou “pneumonia”. Hospitais que não possuem dados sobre determinado desfecho devem ser excluídos do conjunto de hospitais ao decidir os rankings.

Se houver empate no melhor hospital para um determinado resultado, os nomes dos hospitais devem ser classificados em ordem alfabética e o primeiro hospital desse conjunto deve ser escolhido (ou seja, se os hospitais “b”, “c” e “f” estão empatados então o hospital “b” deve ser devolvido). A função deve usar o seguinte modelo:

best                <- function(state, outcome) 
{
    ## Ler dados de resultados.
    ## Verifique se o estado e o resultado são válidos.
    ## Retornar o nome do hospital naquele estado com menor taxa de mortalidade em 30 dias.
}

A função deve verificar a validade de seus argumentos. Se o argumento state for inválido a função deve gerar um erro por meio de parada com a mensagem “invalid state”. Se o argumento ‘outcome’ for inválido a função deve gerar um erro por meio de parada com a mensagem “invalid outcome”.

3.8.2.2. Função rankhospital

Escreva uma função chamada rankhospital que receba três argumentos: o nome abreviado de 2 caracteres de um estado (state), um resultado (outcome) e a classificação do hospital nesse estado para esse resultado (num). A função deve ler o arquivo outcome-of-care-measures.csv e retornar um vetor de caracteres com o nome do hospital que possui a classificação especificada pelo argumento num. Por exemplo:

# rankhospital("MD", "heart failure", 5) 

Retornando um vetor de caracteres contendo o nome do hospital com a 5a menor taxa de mortalidade em 30 dias por insuficiência cardíaca. O argumento ‘num’ pode receber valores “melhor”, “pior” ou um número inteiro indicando a classificação (números menores são melhores). Se o número dado por num for maior que o número de hospitais naquele estado, então a função deve retornar NA. Hospitais que não possuem dados sobre determinado desfecho devem ser excluídos do conjunto de hospitais ao decidir os rankings.

Pode ocorrer que vários hospitais tenham a mesma taxa de mortalidade em 30 dias para uma determinada causa de morte. Nesses casos, os empates devem ser desfeitos com o uso do nome do hospital. Por exemplo, no Texas (“TX”), os hospitais com menor taxa de mortalidade em 30 dias por insuficiência cardíaca são mostrados aqui.

# head(texas)
##                          Hospital.Name Rate Rank
## 3935        FORT DUNCAN MEDICAL CENTER  8.1    1
## 4085   TOMBALL REGIONAL MEDICAL CENTER  8.5    2
## 4103  CYPRESS FAIRBANKS MEDICAL CENTER  8.7    3
## 3954            DETAR HOSPITAL NAVARRO  8.7    4
## 4010            METHODIST HOSPITAL,THE  8.8    5
## 3962   MISSION REGIONAL MEDICAL CENTER  8.8    6

Observe que o Cypress Fairbanks Medical Center e o Detar Hospital Navarro têm a mesma taxa de 30 dias (8,7). No entanto Cypress vem antes de Detar alfabeticamente, Cypress é classificado como número 3 neste esquema e Detar é classificado como número 4. Pode-se usar a função de ordem para classificar vários vetores desta maneira (ou seja, onde um vetor é usado para desempate em outro vetor). A função deve usar o seguinte modelo:

rankhospital        <- function(state, outcome, num = "best") 
{
    ## Ler dados de resultados.
    ## Verifique se o estado e o resultado são válidos.
    ## Devolva o nome do hospital nesse estado com a classificação dada taxa de mortalidade em 30 dias.
}

A função deve verificar a validade de seus argumentos. Se o argumento state for inválido a função deve gerar um erro por meio de parada com a mensagem “invalid state”. Se o argumento ‘outcome’ for inválido a função deve gerar um erro por meio de parada com a mensagem “invalid outcome”.

3.8.2.3. Função rankall

Escreva uma função chamada rankall que receba dois argumentos: o nome do resultado (outcome) e a classificação do hospital (num). A função lê o arquivo outcome-of-care-measures.csv e retorna um quadro de dados de 2 colunas contendo o hospital em cada estado que possui a classificação especificada em ‘num’. Por exemplo, a chamada de função rankall(“heart attack”, “best”) retornaria um quadro de dados contendo os nomes dos hospitais que são os melhores em seus respectivos estados para taxas de mortalidade por ataque cardíaco em 30 dias. A função deve retornar um valor para cada estado (alguns podem ser NA). A primeira coluna no quadro de dados é denominada hospital, que contém o nome do hospital, e a segunda coluna é denominada estado, que contém a abreviação de 2 caracteres para o nome do estado. Hospitais que não possuem dados sobre determinado desfecho devem ser excluídos do conjunto de hospitais ao decidir os rankings. A função ‘rankall’ deve lidar com empates nas taxas de mortalidade em 30 dias da mesma forma que a função rankhospital lida com empates. A função deve usar o seguinte modelo:

rankall             <- function(outcome, num = "best")
{
    ## Ler dados de resultados.
    ## Verifique se o estado e o resultado são válidos.
    ## Para cada estado, encontre o hospital da classificação dada.
    ## Retorna um quadro de dados com os nomes dos hospitais e o nome do estado (abreviado).
}

A função deve verificar a validade de seus argumentos. Se o argumento ‘outcome’ for inválido a função deve gerar um erro por meio de parada com a mensagem “invalid outcome”. A variável ‘num’ pode assumir valores “melhor”, “pior” ou um número inteiro indicando a classificação (números menores são melhores). Se o número dado por ‘num’ for maior que o número de hospitais naquele estado, então a função deve retornar NA.

4. Introdução à Probabilidade

4.1. A Ideia de Probabilidade

“A experiência não permite nunca atingir a certeza absoluta. Não devemos procurar obter mais que uma probabilidade.”

–Bertrand Russell


Historicamente, acredita-se os primeiros cálculos da teoria da probabilidade foram realizados por estudiosos italianos dos séculos XV e XVI, dentre os quais podemos destacar Frei Luca Pacioli (1445 - 1517), Tartaglia (1499 - 1557) e Girolamo Cardano (1501 - 1576). Frei Luca Pacioli, por exemplo, dedicou-se ao estudo do problema conhecido como o problema dos pontos (divisão de apostas), publicando, em 1494, na obra intitulada Summa de arithmetica, geometria, proportinoni e proportionalità, uma solução (incorreta) para este problema. Tal solução apontava que os jogadores deveriam dividir a aposta numa proporção de 5 por 3.

Tartaglia também trabalhou com o problema dos pontos. De acordo com Katz (2009), na obra intitulada General Trattato, publicada em 1556, este matemático afirmou que a solução apresentada por Pacioli para o problema dos pontos poderia estar incorreta. Já Girolamo Cardano, publicou em 1663 a obra Liber de Ludo Alae “[…] que buscava permitir a tomada de boas decisões nos problemas de jogos de azar encontrados naquela época”. De acordo com Todhunter (1965), a obra de Cardano pode ser entendida como um manual de jogos.

Embora os cálculos dessa teoria tenha começado com os italianos, o marco do início da teoria das probabilidades é considerado com a troca de correspondências entre os estudiosos franceses Blaise Pascal (1623 - 1662) e Pierre de Fermat (1601 - 1665). As cartas trocadas por estes estudiosos em 1654 apresentam discussões e uma solução de um problema semelhante ao problema dos pontos que foi apresentado a Pascal por Antoine Gombauld (1610 - 1685), um homem que ganhava a vida jogando e era conhecido como cavaleiro de Méré.

Atualmente, essa teoria deixou de ser um pequeno ramo da consolidada Ciência Matemática e se tornou uma ciência relacionada com fenômenos aleatórios, sendo a peça chave para a previsão e discussão desses fenômenos nas mais diversas áreas do conhecimento. Portanto, para iniciar nossos estudos desta teoria, vamos começar com um dos elementos mais importantes da teoria da probabilidade, o espaço amostral.

4.2. Espaço Amostral

Assim como o processo de amostragem nos gera uma amostra, o processo experimental nos gera resultados que, por analogia, são chamados de pontos amostrais. A partir dessa ideia, temos dois conceitos importantes: evento (que é uma coleção de pontos amostrais, e, em geral, é denotado por uma letra maiúscula), e espaço amostral (que é a coleção de todos os pontos amostrais, e é denotado por \(\Omega\)).

Para exemplificar esses conceitos utilizando o R, vamos considerar que duas moedas são lançadas e suas faces são registradas. Quais seriam todos os pontos amostrais que compõe o espaço amostral para este experimento aleatório? Para responder essa questão, criemos o experimento de lançar uma moeda de acordo com seguinte rotina em R (chamada de tosscoin):

tosscoin  <- function(times, makespace = FALSE)
{
    temp  <- list()
    for (i in 1:times)
    {
      temp[[i]] <- c("H", "T")
    }
    res   <- expand.grid(temp, KEEP.OUT.ATTRS = FALSE)
    names(res) <- c(paste(rep("toss", times), 1:times, sep = ""))
    if (makespace) 
        res$probs <- rep(1, 2^times)/2^times
    return(res)
}

# Espaço Amostral: Lançar a moeda uma vez
tosscoin(2)
##   toss1 toss2
## 1     H     H
## 2     T     H
## 3     H     T
## 4     T     T
# Espaço Amostral: Lançar a moeda três vezes
tosscoin(3)
##   toss1 toss2 toss3
## 1     H     H     H
## 2     T     H     H
## 3     H     T     H
## 4     T     T     H
## 5     H     H     T
## 6     T     H     T
## 7     H     T     T
## 8     T     T     T

No entanto, em algumas situações, determinar o número de resultados que compreendem o espaço amostral (ou o evento) pode não ser uma tarefa muito simples, necessitando de alguns conceitos matemáticos mais elaborados como permutações, e combinações.

7.2.1. Permutações

As permutações como o próprio nome indica, consiste em permutar algo, isto é, formar agrupamentos ordenados com todos os elementos desse conjunto. De acordo com a literatura, existem dois tipos: as permutações simples, e as permutações de objetos similares. As permutações simples são, essencialmente, o número de permutações (troca de posições) de \(n\) objetos diferentes. Tal número é obtido, em toda sua simplicidade, pela equação \(P_n = n!\) (\(n\) fatorial). Por outro lado, em situações em que há repetição, trabalha-se com o conceito de permutação de objetos similares que é descrito, matematicamente, pela seguinte equação:

\[P_{n_r} = \dfrac{n!}{n_1!n_2! \ldots n_r!}\] onde \(n_i, i = 1, 2, \ldots, r\) são a contagem dos objetos similares (ou repetidos). No R, para o primeiro caso (permutação simples), utilizamos a função factorial:

# Uso da função factorial():

# factorial(x)

Pn <- factorial(10)
Pn
## [1] 3628800

Para o segundo caso (permutação com objetos repetidos), podemos trabalhar com o pacote gtools por meio da função permutations com argumento repeats.allowed = TRUE, isto é,

# Instalar e carregar os pacotes necessários

# install.packages('gtools')
library(gtools)
## Warning: package 'gtools' was built under R version 4.2.2
## 
## Attaching package: 'gtools'
## The following object is masked from 'package:psych':
## 
##     logit
# Espaço amostral: Urna com três bolas

x <- c('red', 'blue', 'black')

# Uso da função permutations()

# permutations(n, r, v = 1:n, set = TRUE, repeats.allowed = FALSE)

permutations(n = 3, r = 2, v = x, repeats.allowed = T)
##       [,1]    [,2]   
##  [1,] "black" "black"
##  [2,] "black" "blue" 
##  [3,] "black" "red"  
##  [4,] "blue"  "black"
##  [5,] "blue"  "blue" 
##  [6,] "blue"  "red"  
##  [7,] "red"   "black"
##  [8,] "red"   "blue" 
##  [9,] "red"   "red"

7.2.2. Combinações

Matematicamente, uma combinação simples é definida como a contagem de todos os subconjuntos com \(k\) elementos de um determinado conjunto, com \(k \leq n\). Ou seja,

\[C_{(n,r)}= \dfrac{n!}{(n-r)! \cdot r!}\] No R, tais combinações são obtidas facilmente pela função choose, isto é,

# Uso da função choose():

# choose(n, k)

Cnp <- choose(n = 24, k = 4)
Cnp
## [1] 10626

4.3. O Conceito de Probabilidade

4.3.1. As Interpretações de Probabilidade

Atualmente, a teoria de probabilidade deixou de ser um pequeno ramo da consolidada Ciência Matemática e se tornou uma ciência relacionada com fenômenos aleatórios, sendo a peça chave para a previsão e discussão desses fenômenos nas mais diversas áreas do conhecimento. Na literatura, podemos encontrar três interpretações essenciais para o conceito de probabilidade: a interpretação frequentista, a interpretação clássica, e a interpretação subjetiva.

  • Intepretação Frequentista: Seja \(A\) um evento qualquer. Se \(n_A\) é o número de ocorrências de \(A\) em \(n\) repetições independentes do experimento, então a probabilidade de ocorrência de \(A\) é dada por:

    \[P(A) = \lim_{n \to \infty}\dfrac{n_A}{n}\]
  • Intepretação Clássica: Seja \(\Omega\) um espaço amostral e \(A\) um evento qualquer. Se \(N(\Omega)\) é o número de elementos do espaço amostral \(\Omega\) e \(N(A)\) é o número de elementos do evento \(A\), então a probabilidade de ocorrência do evento A é definida como:

    \[P(A) = \dfrac{N(A)}{N(\Omega)}\]

    Se um experimento tem como espaço amostral \(\Omega = \{e_1,e_2,\ldots,e_n\}\), com um número finito de elementos, dizemos que os eventos elementares \(\{e_i\}\) são equiprováveis, se todos tem a mesma probabilidade de ocorrer, isto é:

    \[P(\{e_i\})=\frac{1}{n}\]

    Desta forma, podemos definir a probabilidade de um evento \(E = \{e_{j1},\ldots,e_{jk}\}\), composto por \(k\) elementos (com \(k\) menor que \(n\)), como sendo:

    \[P(E)=\frac{\text{número de casos favoráveis a E}}{\text{número de casos possíveis de $\Omega$}}=\frac{k}{n}\]
  • Intepretação Subjetiva: Essa visão surgiu no início da década de 1950, com Leonard J. Savage que deu um impulso considerável ao que é chamado de conceito subjetivo de probabilidade, esta sendo expressa como a informação de um indivíduo. Para Leonard J. Savage, essa visão sustenta que a probabilidade mede a confiança que um indivíduo tem na verdade de uma proposição. Em outras palavras, essa interpretação permite que a incerteza e a subjetividade intrínsecas do estudo sejam adicionadas nos processos de decisão.

4.3.2. A Definição Matemática de Probabilidade

Anteriormente, aprendemos formas de interpretar o que é uma probabilidade sob as óticas, objetiva e subjetiva, e também como atribuir a probabilidade sob essas óticas. Mas a nossa pergunta agora é: qual é a definição matemática de probabilidade? Para responder essa questão, nesta seção, iremos entender o que forma um espaço de probabilidade e como isso define formalmente a probabilidade. Para tal tarefa, os axiomas de Kolmogorov serão considerados uma vez que eles são responsáveis por garantir a existência da probabilidade.

Definição: Dizemos que uma função \(P\) definida em uma \(\sigma\)-álgebra \(\Lambda\) de subconjuntos de um espaço amostral \(\Omega\) e imagem restrita ao intervalo \([0,1]\), é uma probabilidade se satisfaz os seguintes axiomas de Kolmogorov:

  1. \(P(\Omega) = 1\).
  2. Para todo subconjunto \(A \in \Lambda\), \(0\leq P(\Omega) \leq 1\).
  3. Para toda sequência \(A_1, A_2, \ldots , A_n, \ldots \in \Lambda\), mutuamente exclusiva, tem-se que:

\[P\left(\bigcup_{i=1}^{\infty} A_i\right) = \sum_{i=1}^{\infty} P\left(A_i\right)\] Para exemplificar o cálculo de probabilidades no R, considere o experimento de retirar uma carta de um baralho padrão criado de acordo com a seguinte rotina em R:

# Rotina em R para o espaço amostral do baralho padrão (52 cartas)

cards <- function (jokers = FALSE, makespace = FALSE)
{
    x <- c(2:10, "J", "Q", "K", "A")
    y <- c("Club", "Diamond", "Heart", "Spade")
    res <- expand.grid(rank = x, suit = y)
    if (jokers)
    {
        levels(res$rank) <- c(levels(res$rank), "Joker")
        res <- rbind(res, data.frame(rank = c("Joker", "Joker"), 
        suit = c(NA, NA)))
    }
    if (makespace) 
    {
        res$probs <- rep(1, dim(res)[1])/dim(res)[1]
    }
    return(res)
}

Denote o espaço de probabilidade associado ao experimento como S, e defina os subconjuntos A e B de acordo com a rotina em R:

# Espaço amostral do experimento

S <- cards(makespace = TRUE)

# Eventos A e B

A <- subset(S, suit == "Heart")
B <- subset(S, rank %in% 7:9)

Para calcularmos a probabilidade de se tirar uma carta de copas (evento A) e a probabilidade de se tirar uma carta que seja um sete, oito ou nove (evento B) de acordo com o espaço amostral gerado pela rotina acima, com base na interpretação clássica e nos axiomas de Kolmogorov, vamos, inicialmente, criar uma função para o cálculo de probabilidades em R:

# Função para o cálculo de probabilidades

prob <- function (x, event = NULL, given = NULL, ...)
{
  if (is.null(x$probs))
  {
      message("O espaço amostral, 'space', está sem a coluna das probabilidades, 'probs'")
      stop("see ?probspace")
  }
  if (missing(event)) 
  {
      r <- TRUE
  }
  else 
  {
      e <- substitute(event)
      r <- eval(e, x, parent.frame())
      if (!is.logical(r)) 
      stop("'event' deve ser avaliado como lógico")
      r <- r & !is.na(r)
      if (!isTRUE(all.equal(sum(x$probs), 1))) 
            warning("'space' não tem probabilidade igual a 1.")
  }
  A <- x[r, ]
  if (missing(given)) 
  {
      p <- sum(A$probs)
  }
  else
  {
      f <- substitute(given)
      g <- eval(f, x, enclos = parent.frame())
      if (!is.logical(g)) 
      {
          if (!is.data.frame(given)) 
                stop("'given' deve ser um data.frame or avaliado com lógico")
          B <- given
      }
      else 
      {
          if (missing(event)) 
                stop("'event' deve ser especificado quando 'given' é uma expressão")
            g <- g & !is.na(g)
            B <- x[g, ]
      }
      if (sum(B$probs <= 0)) 
            stop("prob(given) deve ser positiva")
        p <- sum(intersect(A, B)$probs)/sum(B$probs)
  }
  return(p)
}

Com a função em mãos, calculamos, então, as probabilidades desejadas de acordo com a seguinte rotina em R:

# Probabilidade do evento A

prob(A)
## [1] 0.25
# Probabilidade do evento B

prob(B)
## [1] 0.2307692

4.4. Probabilidade Condicional

Com uma certa frequência, nos deparamos com a situação de um conhecimento adicional capaz de afetar a probabilidade do resultado de um dado experimento. Quando isso acontece, então, precisamos alterar a probabilidade de um evento de interesse. Essa nova probabilidade é conhecida como probabilidade condicional do evento e é um dos conceitos mais importantes de toda a teoria da probabilidade. Para obter tal probabilidade, procedemos da seguinte forma: dividimos a probabilidade da interseção dos eventos A e B, \(P(A \cap B)\), pela probabilidade total do espaço amostral, \(P(B)\). Ou seja, em linguagem matemática, a probabilidade de A dado que B ocorreu é descrita pela expressão:

\[P(A\mid B) = \dfrac{P(A\cap B)}{P(B)}\]

Para exemplificar, suponha que, em uma comunidade, 20% dos indivíduos adultos são hipertensos, 40% são diabéticos e 15% são hipertensos e diabéticos. Consideremos um experimento que consiste em selecionar ao acaso um indivíduo dessa comunidade. Defina os eventos:

  • D: o indivíduo escolhido é portador de diabetes.

  • H: o indivíduo escolhido é portador de hipertensão.

Note que, quando dizemos que 15% são hipertensos e diabéticos, nós estamos nos referindo a uma situação em que ambos os eventos, D e H, simultaneamente ocorrem. Nesse caso, nos referimos à interseção entre os eventos, denotada pelo símbolo \(\cap\). Evento resultante da interseção é, então:

  • \(D \cap H\): o indivíduo escolhido é portador de diabetes e de hipertensão.

Assim, de acordo com a interpretação frequentista de probabilidade, ao selecionar ao acaso um indivíduo dessa comunidade, a probabilidade de ele ser portador de hipertensão é P(H) = 0,2. Porém, por outro lado, se partimos do conhecimento que o indivíduo selecionado é portador de diabetes, qual é agora a probabilidade de ele ser portador de hipertensão? Essa pergunta se refere a uma probabilidade condicional, denotada por P(H|D). Lemos o símbolo “|” como “dado que”. Assim, P(H|D) se refere à pro-babilidade de ocorrer o evento H, dado que o evento D ocorreu. Ou, no nosso exemplo, à probabilidade de o indivíduo selecionado ser portador de hipertensão, dado que ele é portador de diabetes.

No diagrama de Venn ilustrado na Figura 1, sombreamos a região associada ao evento D considerando que partimos do conhecimento de que esse evento ocorreu. Essa região sombreada corresponde a 40% dos indivíduos de toda a comunidade, e consideramos que o indivíduo selecionado pertence a essa parcela. Dentre esses 40%, sabemos que 15% são hipertensos. Portanto, a probabilidade de selecionarmos um indivíduo hipertenso entre aqueles que portam diabetes é 0,15/0,40 = 0,375.

Figura 1: Diagrama de Venn para uma comunidade em que 20% dos indivíduos adultos são hipertensos, 40% são diabéticos e 15% são hipertensos e diabéticos.

## Informações do problema

D <- 0.40
H <- 0.20
DH <- 0.15

## Carregar pacotes

library(VennDiagram)
## Warning: package 'VennDiagram' was built under R version 4.2.2
## Loading required package: grid
## Loading required package: futile.logger
## 
## Attaching package: 'futile.logger'
## The following object is masked from 'package:gtools':
## 
##     scat
library(tidyverse)

## Diagrama de Venn

grid.newpage()
invisible(draw.pairwise.venn(area1 = D, area2 = H, cross.area = DH, 
                             category = c("D", "H"), lty = c("blank", "blank"), 
                             fill = c("light blue", "pink"), alpha = c(0.5, 0.5), 
                             cat.pos = c(0, 0), scaled = FALSE, height = 200, width = 200))

Assim, para encontrarmos a probabilidade condicional P(H|D), trabalhamos com a definição dada anteriormente, isto é,

\[P(H\mid D) = \dfrac{P(D\cap H)}{P(D)}\]

desde que P(D) seja maior que zero. Se P(D) fosse igual a zero, não teríamos um único portador de diabetes na comunidade e não faria sentido buscar a probabilidade condicional P(H|D). No R, temos:

P_DH <- 0.15/0.40
P_DH
## [1] 0.375

Como segundo exemplo, considere o experimento do lançamento de dois dados criado pela seguinte rotina em R:

# Rotina em R para o espaço amostral do lançamento de dois dados

rolldie <- function (times, nsides = 6, makespace = FALSE)
{
    temp = list()
    for (i in 1:times) 
    {
        temp[[i]] <- 1:nsides
    }
    res <- expand.grid(temp, KEEP.OUT.ATTRS = FALSE)
    names(res) <- c(paste(rep("X", times), 1:times, sep = ""))
    if (makespace) 
        res$probs <- rep(1, nsides^times)/nsides^times
    return(res)
}

Denote o espaço de probabilidade associado ao experimento como S, e defina os subconjuntos A e B de acordo com a rotina em R:

# Espaço amostral do experimento

S <- rolldie(2, makespace = TRUE) 
head(S)
##   X1 X2      probs
## 1  1  1 0.02777778
## 2  2  1 0.02777778
## 3  3  1 0.02777778
## 4  4  1 0.02777778
## 5  5  1 0.02777778
## 6  6  1 0.02777778
# Eventos A e B

A <- subset(S, X1 == X2)
A
##    X1 X2      probs
## 1   1  1 0.02777778
## 8   2  2 0.02777778
## 15  3  3 0.02777778
## 22  4  4 0.02777778
## 29  5  5 0.02777778
## 36  6  6 0.02777778
B <- subset(S, X1 + X2 >= 8)
B
##    X1 X2      probs
## 12  6  2 0.02777778
## 17  5  3 0.02777778
## 18  6  3 0.02777778
## 22  4  4 0.02777778
## 23  5  4 0.02777778
## 24  6  4 0.02777778
## 27  3  5 0.02777778
## 28  4  5 0.02777778
## 29  5  5 0.02777778
## 30  6  5 0.02777778
## 32  2  6 0.02777778
## 33  3  6 0.02777778
## 34  4  6 0.02777778
## 35  5  6 0.02777778
## 36  6  6 0.02777778

Com base nesses eventos, qual seria a probabilidade de ocorrência que, no lançamento, ambos os dados tenham a mesma face (evento A) dado que a soma das faces dos dados é maior ou igual a oito (evento B)? E qual a probabilidade da soma das faces ser maior ou igual a oito, dado que as faces dos dados são iguais? Para responder ambas as questões, podemos trabalhar com a função prob() implementada anteriormente, isto é,

# Probabilidade: P(A | B)

prob(A, given = B)
## [1] 0.2
# Probabilidade: P(B | A)

prob(B, given = A)
## [1] 0.5

4.5. Teorema de Bayes

Muitas vezes em nosso experimento, começamos a análise com estimativas de probabilidade iniciais (priori) ou anteriores para eventos específicos de interesse. Então, de fontes como uma amostra representativa, um relatório especial ou um teste de produto, obtemos informações adicionais sobre os eventos. Dadas essas novas informações, atualizamos os valores de probabilidade anteriores calculando as probabilidades revisadas, chamadas de probabilidades posteriores (posteriori). Este procedimento é conhecido como Teorema de Bayes. De modo geral, o Teorema de Bayes é aplicável quando os eventos para os quais queremos calcular probabilidades posteriores são mutuamente exclusivos e sua união é todo o espaço amostral, isto é, para o caso de \(n\) eventos mutuamente exclusivos \(A_1, A_2, \ldots, A_n\), cuja união é todo o espaço amostral, o Teorema de Bayes pode ser utilizado para calcular qualquer probabilidade posterior \(P(A_i \mid B)\) da seguinte forma:

\[P(A_i \mid B) = \dfrac{P(A_i) P(B \mid A_i)}{P(A_1) P(B \mid A_1)\ldots P(A_n) P(B \mid A_n)}\]

Para trabalhar com esse teorema no R, podemos fazer o uso da função BayesTheorem disponível no pacote LaplacesDemon. Essa função necessita, basicamente, de dois argumentos: a probabilidade a priori do evento A, e a probabilidade condicional do evento B dado o evento A (também é conhecida como evidência). Para exemplificar, vamos considerar o seguinte problema:

Dois tipos de vacina foram aplicados em uma população, de tal forma que 60% das pessoas receberam vacina do tipo A e as 40% restantes receberam vacina do tipo B. Sabendo que a vacina do tipo A fornece 70% de imunização, e a B fornece 80%, qual a probabilidade de que uma pessoa, escolhida ao acaso, tenha sido vacinada por A dado que ela não esteja imunizada?

Para responder essa questão, utilizamos a função BayesTheorem, podemos calcular essa probabilidade pela seguinte rotina em R:

## Carregar o pacote

library(LaplacesDemon)
## Warning: package 'LaplacesDemon' was built under R version 4.2.2
## 
## Attaching package: 'LaplacesDemon'
## The following objects are masked from 'package:gtools':
## 
##     ddirichlet, logit, rdirichlet
## The following object is masked from 'package:purrr':
## 
##     partial
## The following objects are masked from 'package:psych':
## 
##     logit, tr
## Determinar as probabilidades

PrA <- c(0.60, 0.40) # Probabilidade de ter sido vacidada por A (60%) e seu complemento
PrBA <- c(0.30, 0.20)  # Probabilidade condicional referente a imunização de ambas as vacinas

## Utilizar o Teorema de Bayes

# Uso da função BayesTheorem():

# BayesTheorem(PrA, PrBA)

BayesTheorem(PrA, PrBA)[1] # [1] retorna a probabilidade, [2] retorna a probabilidade complementar
## [1] 0.6923077

4.6. Distribuições de Probabilidade

4.6.1. Distribuição Binomial

Seja \(X\) uma variável aleatória discreta tal que \(X\) conta o número de tentativas que resultam em um sucesso em \(n\) tentativas. Neste caso, a distribuição de probabilidade de \(X\) é conhecida como distribuição Binomial com parâmetro \(\rho\) e função de probabilidade caracterizada por:

\[P(x) = \binom{n}{x} \rho^{x}(1-\rho)^{n-x}\]

em que \(0 < \rho < 1\). Note que se

\[X\sim Bin(n,\rho)\]

então,

\[E(X) = n\rho\]

\[Var(X) = n\rho(1-\rho)\] No R, a distribuição binomial pode ser acessada pelas seguintes funções: dbinom (função de probabilidade), pbinom (função de distribuição acumulada), qbinom (função quartil), e rbinom (função geradora de valores aleatórios).

# Distribuição Binomial

# dbinom(x, size, prob, log = FALSE)
# pbinom(q, size, prob, lower.tail = TRUE, log.p = FALSE)
# qbinom(p, size, prob, lower.tail = TRUE, log.p = FALSE)
# rbinom(n, size, prob)

# Cálculo de probabilidade: Distribuição binomial

dbinom(x = 3, size = 10, prob = 0.5)
## [1] 0.1171875
# Gráfico

plot(dbinom(1:80, size = 80, prob = 0.2), type = "h", lwd = 2,
     main = "Distribuição de Probabilidade: Binomial",
     ylab = "P(X = x)", xlab = "Número de Sucessos")

4.6.2. Distribuição Geométrica

Seja \(X\) uma variável aleatória discreta tal que \(X\) conte o número de fracassos anteriores ao primeiro sucesso. Neste caso, a distribuição de probabilidade de \(X\) é conhecida como distribuição Geométrica com parâmetro \(\rho\) e tem função de probabilidade escrita na forma:

\[P(x) = \rho(1-\rho)^{x}, 0 < \rho < 1\]

Note que se

\[X\sim Geo(\rho)\]

então

\[E(X)=\dfrac{1-\rho}{\rho}\]

\[Var(X) = \dfrac{1- \rho}{\rho^2}\]

No R, a distribuição geométrica pode ser acessada pelas seguintes funções: dgeom (função de probabilidade), pgeom (função de distribuição acumulada), qgeom (função quartil), e rgeom (função geradora de valores aleatórios).

# Distribuição Goemétrica

# dgeom(x, prob, log = FALSE)
# pgeom(q, prob, lower.tail = TRUE, log.p = FALSE)
# qgeom(p, prob, lower.tail = TRUE, log.p = FALSE)
# rgeom(n, prob)

# Cálculo de probabilidade: Distribuição geométrica

dgeom(x = 2, prob = 0.5)
## [1] 0.125
# Gráfico

plot(dgeom(1:80, prob = 0.2), type = "h", lwd = 2,
     main = "Distribuição de Probabilidade: Geométrica",
     ylab = "P(X = x)", xlab = "x")

4.6.3. Distribuição de Poisson

Seja \(X\) uma variável aleatória discreta tal que \(X\) registre o número de eventos em um intervalo de tempo. A distribuição de probabilidade de \(X\) é conhecida como distribuição de Poisson com parâmetro \(\lambda\) e tem função de probabilidade escrita na forma:

\[P(x) = \dfrac{e^{-\lambda} \lambda^x}{x!}, \lambda > 0\]

Note que se

\[X \sim Poisson(\lambda)\]

então

\[E(X) = \lambda\] \[Var(X) = \lambda\]

No R, a distribuição de Poisson pode ser acessada pelas seguintes funções: dpois (função de probabilidade), ppois (função de distribuição acumulada), qpois (função quartil), e rpois (função geradora de valores aleatórios).

# Distribuição de Poisson

# dpois(x, lambda, log = FALSE)
# ppois(q, lambda, lower.tail = TRUE, log.p = FALSE)
# qpois(p, lambda, lower.tail = TRUE, log.p = FALSE)
# rpois(n, lambda)

# Cálculo de probabilidade: Distribuição de Poisson

dpois(x = 2, lambda = 0.5)
## [1] 0.07581633
# Gráfico

plot(dpois(1:80, lambda = 40), type = "h", lwd = 2,
     main = "Distribuição de Probabilidade: Poisson",
     ylab = "P(X = x)", xlab = "x")

4.6.4. Distribuição Uniforme

Dizemos que uma variável aleatória contínua \(X\) é distribuída uniformemente ao longo do intervalo \((a,b)\) se sua função densidade de probabilidade é dada por:

\[f_x(x) = \begin{cases} \dfrac{1}{b-a}, \quad \mbox{se } \mbox{ $a<x<b$} \\ 0, \hspace{1.33cm} \mbox{se } \mbox{ caso contrário} \end{cases}\]

Nesse caso, se

\[X \sim U(a,b)\]

então

\[E(X) = \dfrac{b+a}{2}\] \[Var(X) = \dfrac{(b-a)^2}{12}\]

No R, a distribuição Uniforme pode ser acessada pelas seguintes funções: dunif (função de probabilidade), punif (função de distribuição acumulada), qunif (função quartil), e runif (função geradora de valores aleatórios).

# Distribuição Uniforme

# dunif(x, min = 0, max = 1, log = FALSE)
# punif(q, min = 0, max = 1, lower.tail = TRUE, log.p = FALSE)
# qunif(p, min = 0, max = 1, lower.tail = TRUE, log.p = FALSE)
# runif(n, min = 0, max = 1)

# Cálculo de probabilidade: Distribuição Uniforme

dunif(x = 2, min = 0, max = 4)
## [1] 0.25
# Gráfico

plot(dunif(seq(0, 10, by = 0.1), min = 0, max = 100), type = "l", lwd = 2,
     main = "Distribuição de Probabilidade: Uniforme",
     ylab = "f(x)", xlab = "x")

4.6.5. Distribuição Weibull

Dizemos que uma variável aleatória contínua \(X\) segue uma distribuição Weibull com parâmetros \(\lambda >0\) e \(\alpha >0\) se sua função densidade de probabilidade é dada por:

\[f_x(x) = \dfrac{\alpha}{\lambda^{\alpha}} x^{\alpha -1} e^{\left[-(\frac{x}{\lambda})^{\alpha}\right]}, x > 0\]

Note que se \[X \sim Weibull(\lambda,\alpha)\]

então

\[E(X) = \lambda \Gamma(1+\alpha^{-1})\] \[Var(X) = \lambda^2[\Gamma(1+2\alpha^{-1}) - \Gamma(1+\alpha^{-1})]\]

No R, a distribuição Weibull pode ser acessada pelas seguintes funções: dweibull (função de probabilidade), pweibull (função de distribuição acumulada), qweibull (função quartil), e rweibull (função geradora de valores aleatórios).

# Distribuição Weibull

# dweibull(x, shape, scale = 1, log = FALSE)
# pweibull(q, shape, scale = 1, lower.tail = TRUE, log.p = FALSE)
# qweibull(p, shape, scale = 1, lower.tail = TRUE, log.p = FALSE)
# rweibull(n, shape, scale = 1)

# Cálculo de probabilidade: Distribuição Weibull

dweibull(x = 1.8, shape = 2, scale = 0.4)
## [1] 3.611763e-08
# Gráfico

plot(seq(0, 1, by = 0.01), dweibull(seq(0, 1, by = 0.01), shape = 0.8, scale = 0.4), type = "l", lwd = 2,
     main = "Distribuição de Probabilidade: Weibull",
     ylab = "f(x)", xlab = "x")

4.6.6. Distribuição Qui-Quadrado

Uma variável aleatória contínua \(X\) segue uma distribuição qui-quadrado com \(\nu\) graus de liberdade se sua função densidade for escrita na forma:

\[f_x(x)=\frac{1}{2^{\nu/2}\Gamma(\nu/2)}x^{(v/2)-1}e^{(-\frac{x}{2})}; \nu > 0, x > 0\]

sendo \(\Gamma(\omega)=\int_0^{\infty}x^{\omega-1}e^{-x}dx, \omega > 0\). No R, a distribuição Qui-Quadrado pode ser acessada pelas seguintes funções: dchisq (função de probabilidade), pchisq (função de distribuição acumulada), qchisq (função quartil), e rchisq (função geradora de valores aleatórios).

# Distribuição Qui-Quadrado

# dchisq(x, df, ncp = 0, log = FALSE)
# pchisq(q, df, ncp = 0, lower.tail = TRUE, log.p = FALSE)
# qchisq(p, df, ncp = 0, lower.tail = TRUE, log.p = FALSE)
# rchisq(n, df, ncp = 0)

# Cálculo de probabilidade: Distribuição Qui-Quadrado

dchisq(x = 1.8, df = 5)
## [1] 0.1305667
# Gráfico

plot(seq(0, 20, by = 0.1), dchisq(seq(0, 20, by = 0.1), df = 3), type = "l", lwd = 2,
     main = "Distribuição de Probabilidade: Qui-Quadrado",
     ylab = "f(x)", xlab = "x")

4.6.7. Distribuição t de Student

Uma variável aleatória contínua \(X\) tem distribuição \(t\) de Student com \(\nu\) graus de liberdade se sua função densidade de probabilidade é dada por:

\[f_x(x)=\frac{\Gamma\left(\frac{\nu+1}{2}\right)}{\sqrt{\nu\pi}\Gamma\left(\frac{\nu}{2}\right)}\left(1+\frac{x^2}{\nu}\right)^{-\left(\frac{\nu+1}{2}\right)},\qquad x \in(-\infty,\infty)\] No R, a distribuição t de Student pode ser acessada pelas seguintes funções: dt (função de probabilidade), pt (função de distribuição acumulada), qt (função quartil), e rt (função geradora de valores aleatórios).

# Distribuição t de Student

# dt(x, df, ncp, log = FALSE)
# pt(q, df, ncp, lower.tail = TRUE, log.p = FALSE)
# qt(p, df, ncp, lower.tail = TRUE, log.p = FALSE)
# rt(n, df, ncp)

# Cálculo de probabilidade: Distribuição t de Student

dt(x = 1.8, df = 5)
## [1] 0.08481296
# Gráfico

plot(seq(-5, 5, by = 0.1), dt(seq(-5, 5, by = 0.1), df = 5), type = "l", lwd = 2,
     main = "Distribuição de Probabilidade: t de Student",
     ylab = "f(x)", xlab = "x")

4.6.8. Distribuição Normal

Uma variável aleatória contínua \(X\) tem distribuição normal com parâmetros \(-\infty < \mu < \infty\) e \(\sigma^2 >0\) se sua função densidade de probabilidade for dada por:

\[f_x(x)=\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}, \quad x\in(-\infty,\infty)\]

Assim, se

\[X\sim N(\mu,\sigma^2)\]

então

\[E(X) = \mu\] \[Var(X) = \sigma^2\]

Além disso, \(f_x(x)\) é simétrica em relação à \(\mu\) e, quando \(\mu=0\) e \(\sigma^2 = 1\), sua densidade se reduz a:

\[f(x)=\frac{1}{\sqrt{2\pi}}e^{-\frac{1}{2}x^2}\]

e é conhecida como distribuição Normal padrão. Neste caso, dizemos que \(X \sim N(0,1)\). No R, a distribuição normal pode ser acessada pelas seguintes funções: dnorm (função de probabilidade), pnorm (função de distribuição acumulada), qnorm (função quartil), e rnorm (função geradora de valores aleatórios).

# Distribuição Normal

# dnorm(x, mean = 0, sd = 1, log = FALSE)
# pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
# qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
# rnorm(n, mean = 0, sd = 1)

# Cálculo de probabilidade: Distribuição Normal

dnorm(x = 1.8, mean = 5, sd = 1)
## [1] 0.002384088
# Gráfico

plot(seq(0, 20, by = 0.1), dnorm(seq(0, 20, by = 0.1), mean = 10, sd = 3), type = "l", lwd = 2,
     main = "Distribuição de Probabilidade: Normal",
     ylab = "f(x)", xlab = "x")

4.7. Teorema do Limite Central

Na teoria da probabilidade, o Teorema do Limite Central (TLC) estabelece que, em muitas situações, quando variáveis aleatórias independentes são somadas, sua soma devidamente normalizada tende a uma distribuição normal mesmo que as variáveis originais não sejam normais. O teorema é um conceito-chave na teoria da probabilidade porque implica que métodos probabilísticos e estatísticos que funcionam para distribuições normais podem ser aplicáveis a muitos problemas envolvendo outros tipos de distribuições. Este teorema sofreu muitas mudanças durante o desenvolvimento formal da teoria da probabilidade. As versões anteriores do teorema datam de 1811, mas em sua forma geral moderna, esse resultado fundamental na teoria da probabilidade foi declarado com precisão até 1920, servindo assim como uma ponte entre a teoria clássica e a teoria moderna da probabilidade. Para explificar o uso do teorema, consideraremos algumas distribuições clássicas de probabilidade.

4.7.1. Distribuição Uniforme

Considere a seguinte amostra gerada de uma distribuição uniforme:

unif <- c(1:8)
unif
## [1] 1 2 3 4 5 6 7 8

Com base nos conceitos de Estatística Descritiva, obtemos que a média e o desvio-padrão amostrais são descritos por:

mean(unif)
## [1] 4.5
sd(unif)
## [1] 2.44949

Graficamente, temos:

hist(unif, main = "Distribuição Uniforme", xlab = " ")

Para entender o teorema central do limite, faremos um laço de repetição do tipo for para continuar calculando a média amostral de cada amostra coletada. Para este processo, recomenda-se utilizar um número alto de repetições e, também, definir uma semente (pelo comando set.seed()) para garantir que o mesmo resultado seja obtido quando qualquer usuário iniciar com a mesma semente toda vez que o mesmo processo for executado. Além disso, devemos iniciar o processo a partir de um determinado tamanho amostral e ir aumentando até for possível garantir a convergência.

set.seed(123)

par(mfrow = c(2,3))

# Tamanho de Amostra 3
sample_means <- c()

for(i in 1:1000)
{
    sample_means[i] <- mean(sample(8, 3, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 3", xlab = "Médias Amostrais")

# Tamanho de Amostra 10
for(i in 1:1000)
{
    sample_means[i] <- mean(sample(8, 10, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 10", xlab = "Médias Amostrais")

# Tamanho de Amostra 30
for(i in 1:1000)
{
    sample_means[i] <- mean(sample(8, 30, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 30", xlab = "Médias Amostrais")

# Tamanho de Amostra 50
for(i in 1:1000)
{
    sample_means[i] <- mean(sample(8, 50, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 50", xlab = "Médias Amostrais")

# Tamanho de Amostra 75
for(i in 1:1000)
{
    sample_means[i] <- mean(sample(8, 75, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 75", xlab = "Médias Amostrais")

# Tamanho de Amostra 100
for(i in 1:1000)
{
    sample_means[i] <- mean(sample(8, 100, replace = TRUE))
}
hist(sample_means, xlim = c(0,8), main = "Tamanho de Amostra 100", xlab = "Médias Amostrais")

4.7.2. Distribuição Binomial

Vamos dar uma olhada na distribuição binomial para avaliar o TCL. Neste caso, Para criar a distribuição de probabilidade binomial, usaremos a função dbinom(x, size, prob) onde x = vetor do número de sucessos, size = tamanho da amostra, prob = probabilidade de sucesso. Já para a representação gráfica, usaremos a função plot(x, y, type = "h") onde x = vetor do número de sucessos, y = dbinom() e type = “h” para histograma como linhas verticais.

par(mfrow = c(2,3))

# Tamanho de Amostra 10
success <- c(0:10)
plot(success, dbinom(success, size = 10, prob = 0.25), 
     type = "h",
     main = "Tamanho de Amostra 10, p = 0.25",
     xlab = "Número de Sucessos",
     ylab = "Probabilidade de Sucesso",
     lwd = 3)

# Tamanho de Amostra 30
suc <- c(0:30)
y <-  dbinom(suc, size = 30, prob = 0.25)
plot(suc, y, 
     type = "h",
     main = "Tamanho de Amostra 30, p = 0.25",
     xlab = "Número de Sucessos",
     ylab = "Probabilidade de Sucesso")

# Tamanho de Amostra 100
suc <- c(0:100)
y <-  dbinom(suc, size = 100, prob = 0.25)
plot(suc, y, 
     type = "h",
     main = "Tamanho de Amostra 100, p = 0.25",
     xlab = "Número de Sucessos",
     ylab = "Probabilidade de Sucesso")

# Tamanho de Amostra 200
suc <- c(0:200)
y <-  dbinom(suc, size = 200, prob = 0.25)
plot(suc, y, 
     type = "h",
     main = "Tamanho de Amostra 200, p = 0.25",
     xlab = "Número de Sucessos",
     ylab = "Probabilidade de Sucesso")

# Tamanho de Amostra 300
suc <- c(0:300)
y <-  dbinom(suc, size = 300, prob = 0.25)
plot(suc, y, 
     type = "h",
     main = "Tamanho de Amostra 300, p = 0.25",
     xlab = "Número de Sucessos",
     ylab = "Probabilidade de Sucesso")

# Tamanho de Amostra 1000
suc <- c(0:1000)
y <-  dbinom(suc, size = 1000, prob = 0.25)
plot(suc, y, 
     type = "h",
     main = "Tamanho de Amostra 1000, p = 0.25",
     xlab = "Número de Sucessos",
     ylab = "Probabilidade de Sucesso")

4.7.3. Distribuição Weibull

Vamos dar uma olhada agora na distribuição Weibull para avaliar o TCL. Neste caso, Para criar a distribuição de probabilidade Weibull, usaremos a função dweibull(x, shape, scale) onde x = vetor de valores, shape = parâmetro de forma, scale = parâmetro de escala.

set.seed(123)

par(mfrow = c(2,3))

# Tamanho de Amostra 3
sample_means <- c()

for(i in 1:1000)
{
    sample_means[i] <- mean(rweibull(n = 3, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 3", xlab = "Médias Amostrais")

# Tamanho de Amostra 10
for(i in 1:1000)
{
    sample_means[i] <- mean(rweibull(n = 10, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 10", xlab = "Médias Amostrais")

# Tamanho de Amostra 30
for(i in 1:1000)
{
    sample_means[i] <- mean(rweibull(n = 30, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 30", xlab = "Médias Amostrais")

# Tamanho de Amostra 50
for(i in 1:1000)
{
    sample_means[i] <- mean(rweibull(n = 50, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 50", xlab = "Médias Amostrais")

# Tamanho de Amostra 75
for(i in 1:1000)
{
    sample_means[i] <- mean(rweibull(n = 75, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 75", xlab = "Médias Amostrais")

# Tamanho de Amostra 100
for(i in 1:1000)
{
    sample_means[i] <- mean(rweibull(n = 100, shape = 2, scale = 2))
}
hist(sample_means, main = "Tamanho de Amostra 100", xlab = "Médias Amostrais")

5. Teste de Hipóteses

Nosso foco, agora, é entender as principais ferramentas para o auxilio de tomada de decisões sobre um grande volume de dados que definem a Inferência Estatística. As primeiras ferramentas do processo de Inferência Estatística são caracterizadas pelos modelos de probabilidade que já vimos anteriormente. As outras ferramentas envolvem as distribuições amostrais e os testes de hipóteses (TH) que serão nosso foco aqui.

A nossa primeira questão sobre os testes de hipóteses é: o que é um teste de hipóteses? Por definição, um teste de hipóteses é uma regra de decisão que permitem rejeitar ou não as hipóteses testadas, com um determinado nível de confiança, baseados em valores amostrais. Neste contexto, então, começa-se fazendo uma suposição provisória sobre um parâmetro populacional. Essa suposição provisória é chamada de hipótese nula e é denotada por \(H_0\). Em seguida, definimos outra hipótese, chamada de hipótese alternativa, que é o oposto do que é afirmado na hipótese nula. Tal hipótese é denotada por \(H_a\).

Nem sempre é óbvio como as hipóteses nula e alternativa devem ser formuladas. É preciso tomar cuidado para estruturar as hipóteses apropriadamente, de modo que a conclusão do teste de hipótese forneça as informações que o pesquisador ou tomador de decisão deseja. Naturalmente, o contexto da situação é muito importante para determinar como as hipóteses devem ser enunciadas. Todas as aplicações de teste de hipótese envolvem coletar uma amostra e usar os resultados amostrais a fim de fornecer evidências para chegar a uma conclusão. Alguns exemplos de boas questões a serem consideradas ao se formular as hipóteses nula e alternativa são: (1) Qual é o objetivo pretendido ao se coletar a amostra? (2) A quais conclusões esperamos chegar? (3) Quais são os parâmetros de interesse?

5.1. Tipos de Erros

Por outro lado, é válido destacar que, em todo teste de hipótese, as hipóteses, nula e alternativa, são afirmações excludentes sobre a população, ou seja, ou a hipótese nula é verdadeira ou a hipótese alternativa é verdadeira, mas nunca ambas ao mesmo tempo. Em geral, espera-se que o teste de hipóteses leve à não-rejeição da hipótese nula quando ela for verdadeira e à rejeição de dela quando ela for falsa. No entanto, tais conclusões nem sempre são possíveis, e isso nos gera dois tipos de erros: erro do tipo I, e o erro do tipo II.

O erro do tipo I é o erro que ocorre quando rejeitamos a hipótese nula e ela é verdadeira, isto é, rejeitar a hipótese nula, neste caso, não é uma decisão correta. Esse tipo de erro é denotado por \(\alpha\) e é conhecido como nível de significância do teste de hipóteses. Já o erro do tipo II é o erro que ocorre quando não-rejeitamos a hipótese nula quando ela é falsa, isto é, descartamos a hipótese alternativa que é verdadeira, também não é uma decisão correta. Esse é denotado por \(\beta\) e não pode ser controlado.

Condição da População
Tabela: Erros e conclusões no teste de hipóteses.
H0 Verdadeira H0 Falsa
Conclusão Não Rejeita H0 Conclusão Correta Erro do Tipo II
Rejeita H0 Erro do Tipo I Conclusão Correta

5.2. Tipos de Hipóteses

Na prática, existem duas formas de realizarmos um teste de hipóteses. Se nosso interesse for em avaliar se há uma diferença em relação ao valor definido para a \(H_0\), tanto positiva como negativa, o teste será chamado de bilateral e suas hipóteses serão definidas como:

\[H_0: \text{Parâmetro Populacional = Parâmetro de Teste}\] \[vs.\] \[H_a: \text{Parâmetro Populacional ≠ Parâmetro de Teste}\]

Às vezes, porém, pode-se supor que uma diferença real possa ocorrer somente em um sentido, de tal forma que, se ocorrer uma diferença no outro sentido, isso é devido ao acaso. Nesse caso, o teste será chamado de unilateral e suas hipóteses serão definidas como:

\[H_0: \text{Parâmetro Populacional } \leq \text{(ou } \geq \text{) Parâmetro de Teste}\] \[vs.\] \[H_a: \text{Parâmetro Populacional } > \text{(ou } < \text{) Parâmetro de Teste}\] Agora que já sabemos o que é um teste de hipóteses, os tipos de erros e os tipos de hipóteses, nos resta saber como realizar um teste de hipóteses. Para tal tarefa, podemos trabalhar com as seguintes etapas:

  • Etapa 1: Declarar as hipóteses nula e alternativa de acordo com o objetivo do experimento.
  • Etapa 2: Definir o nível de significância do teste (erro do tipo I)
  • Etapa 3: Definir a estatística do teste que será utilizada de acordo com distribuição amostral de interesse para o teste.
  • Etapa 4: Definir a região crítica do teste de hipóteses que são os valores numéricos da estatística do teste para os quais a hipótese nula será rejeitada.
  • Etapa 5: Calcular a estatística do teste definida na Etapa 3.
  • Etapa 6: Fazer a decisão estatística do teste, isto é, rejeitar ou não \(H_0\) de acordo com a região crítica definida na Etapa 4 e no valor da estatística do teste.
  • Etapa 7: Conclusão prática do teste, isto é, o que o resultado obtido na Etapa 6 diz na prática sobre o experimento.

5.3. Teste de Média

O nosso primeiro teste paramétrico a ser estudo é o teste de hipóteses para média de uma amostra. Este teste, em geral, é utilizado para comprovar se determinada população possui ou não o valor da média definida na hipótese nula. Para a construção deste teste, há duas possibilidades: amostras grandes, e amostras pequenas. No primeiro caso, o nosso teste será baseado na estatística Z (Teste Z), e no segundo caso, o nosso teste será baseado na estatística T (Teste T).

Amostras Grandes Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \mu \geq \mu_0\)
\(H_a: \mu < \mu_0\)
\(H_0: \mu \leq \mu_0\)
\(H_a: \mu > \mu_0\)
\(H_0: \mu = \mu_0\)
\(H_a: \mu \neq \mu_0\)
Estatística do Teste \(z_{calc} = \dfrac{\bar{x} - \mu_0}{\sigma/\sqrt{n}}\)
Região Crítica Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha}\) Rejeita-se \(H_0\) se \(z_{calc} \geq z_{1-\alpha}\) Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha/2}\)
ou se \(z_{calc} \geq z_{1-\alpha/2}\)

Para trabalhar com o Teste Z no R, fazemos o uso da função z.test do pacote BSDA que é uma função baseada na distribuição normal padrão com objetivo de criar intervalos de confiança e testes de hipóteses para problemas com uma ou duas amostras. Para exemplificar, vamos considerar a seguinte situação:

Sabe-se que a infecção por E. canis é uma doença de cães transmitida por carrapatos que às vezes é contraída por humanos. Entre os humanos infectados, a distribuição da contagem de glóbulos brancos tem uma média desconhecida \(\mu\) e um desvio padrão \(\sigma\). Dado que na população geral a contagem média de glóbulos brancos é de 7250/mm³, um pesquisador clínico elaborou a hipótese de que as pessoas infectadas com E. canis devem ter, em média, contagens mais baixas de glóbulos brancos do que a população geral.

Perguntas de Interesse:

Quais seriam as hipóteses nula e alternativa para o teste de hipóteses com o objetivo de dar suporte a hipótese elaborada pelo pesquisador clínico? Essas hipóteses formam um teste de hipóteses bilateral ou um teste de hipóteses unilateral?

Resposta: Nesse caso, o teste será unilateral e suas hipóteses serão definidas como:

\[H_0: \mu < 7250/mm³\] \[vs.\] \[H_a: \mu \geq 7250/mm³\] Sabendo que, para uma amostra de 45 infectados (vetor \(x\) na rotina em R abaixo) com E. canis, a média de glóbulos brancos foi, aproximadamente, 7013/mm³, e baseando-se em experiências anteriores, obteve-se um desvio padrão populacional \(\sigma\) = 3204/mm³, qual é a conclusão do teste de hipóteses ao nível de significância de 5%?

## Carregar o pacote

library(BSDA)
## Warning: package 'BSDA' was built under R version 4.2.2
## Loading required package: lattice
## 
## Attaching package: 'lattice'
## The following object is masked from 'package:epiDisplay':
## 
##     dotplot
## 
## Attaching package: 'BSDA'
## The following object is masked from 'package:datasets':
## 
##     Orange
## Carregar os dados:

x <- c(6888, 6954, 7312, 7014, 7026, 7343, 7092, 6747, 6863, 6911, 7245, 7072, 
       7080, 7022, 6889, 7357, 7100, 6607, 7140, 6905, 6786, 6956, 6795, 6854, 
       6875, 6663, 7168, 7031, 6772, 7251, 7085, 6941, 7179, 7176, 7164, 7138, 
       7111, 6988, 6939, 6924, 6861, 6958, 6747, 7434, 7242)

## Tamanho da amostra:  

length(x)
## [1] 45
## Como n > 30, fazer o uso do teste Z pela função z.test

# Uso da função z.test():

# z.test(x, y = NULL, alternative = "two.sided", mu = 0, sigma.x = NULL, sigma.y = NULL, conf.level = 0.95)

z.test(x = x, alternative = 'greater', mu = 7250, conf.level = 0.95, sigma.x = 3204)
## 
##  One-sample z-Test
## 
## data:  x
## z = -0.49528, p-value = 0.6898
## alternative hypothesis: true mean is greater than 7250
## 95 percent confidence interval:
##  6227.823       NA
## sample estimates:
## mean of x 
##  7013.444
## Valor de Z tabelado para hipótese do tipo menor:

qnorm(0.05)
## [1] -1.644854
## Conclusão:

# Como o valor calculado para Z, -0.4953, é maior do que o valor tabelado de Z, -1.65, não rejeita-se H0. 
# Isto é, ao nível de significância de 5%, não há evidências suficientes para dizer que a média de 
# glóbulos brancos de pacientes infectados com E. canis é superior a da população geral, de 7250/mm³.
Amostras Pequenas Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \mu \geq \mu_0\)
\(H_a: \mu < \mu_0\)
\(H_0: \mu \leq \mu_0\)
\(H_a: \mu > \mu_0\)
\(H_0: \mu = \mu_0\)
\(H_a: \mu \neq \mu_0\)
Estatística do Teste \(t_{calc} = \dfrac{\bar{x} - \mu_0}{s/\sqrt{n}}\)
Região Crítica Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha}\) Rejeita-se \(H_0\) se \(t_{calc} \geq t_{\alpha}\) Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha/2}\)
ou se \(t_{calc} \geq t_{\alpha/2}\)

Para trabalhar com o Teste t no R, fazemos o uso da função t.test disponível no R que é uma função baseada na distribuição t de Student com objetivo de criar intervalos de confiança e testes de hipóteses para problemas com uma ou duas amostras quando o desvio-padrão populacional é desconhecido. Para exemplificar, vamos considerar a seguinte situação:

A insuficiência cardíaca é uma síndrome clínica caracterizada pela incapacidade do coração de atuar adequadamente como bomba, quer seja por déficit de contração e/ou de relaxamento, comprometendo o funcionamento do organismo. Neste contexto, Ahmad (2017) realizou um estudo sobre os fatores que associados a insuficiência cardíaca de pacientes admitidos no Instituto de Cardiologia do Paquistão no período de Abril a Dezembro de 2015. Os dados do estudo são fornecidos na Tabela 1.

Tabela 1: Dados amostrais referentes a 20 pacientes com insuficiência cardíaca.
Time Event Gender Smoking Diabetes BP Anaemia Age Ejection.Fraction Sodium Creatinine Pletelets CPK
261 109 Censored Female No Yes No No 50.000 30 132 0.90 329000 482
193 207 Non-Censored Female No No No No 60.000 30 127 1.70 62000 166
32 250 Censored Male No No No No 60.000 35 140 1.70 279000 253
29 30 Non-Censored Male No Yes No No 69.000 35 134 3.50 228000 582
257 67 Non-Censored Male No Yes No No 65.000 25 135 1.83 497000 113
144 205 Censored Male Yes No No Yes 58.000 25 132 1.30 189000 57
170 285 Censored Male Yes No No No 50.000 45 136 1.60 395000 196
134 186 Censored Male No No Yes No 70.000 60 138 0.90 220000 97
228 33 Non-Censored Female No Yes No Yes 60.000 60 142 1.10 194000 588
226 72 Censored Male Yes Yes No No 65.000 50 137 1.30 149000 224
277 147 Censored Male Yes No No No 55.000 40 140 0.70 279000 835
52 28 Non-Censored Female No Yes Yes Yes 50.000 35 128 1.00 319000 249
208 87 Censored Female No No Yes No 60.000 50 143 2.30 286000 53
233 193 Non-Censored Female No Yes Yes Yes 48.000 30 130 1.60 244000 131
71 172 Non-Censored Female No No No No 50.000 50 134 0.60 153000 582
114 194 Censored Male No No Yes Yes 65.000 35 134 0.80 290000 135
142 230 Censored Male No Yes No Yes 45.000 25 135 0.80 233000 66
136 171 Non-Censored Male No Yes No Yes 60.667 30 136 1.50 389000 104
108 121 Censored Male No No No Yes 85.000 50 134 1.30 235000 910
218 45 Non-Censored Female Yes Yes No Yes 60.000 38 132 2.20 255000 260

Perguntas de Interesse:

Um pesquisador clínico sugere que a média dos valores de CPK dos pacientes com insuficiência cardíaca é superior a 450. Suponha que o interesse seja testar essa hipótese, quais seriam as hipóteses apropriadas para o teste, neste caso?

Resposta: Nesse caso, o teste será unilateral e suas hipóteses serão definidas como:

\[H_0: \mu > 450\] \[vs.\] \[H_a: \mu \leq 450\]

Considerando a amostra descrita na Tabela 1, quais seriam os valores da média e desvio-padrão amostral? Baseando-se nestes resultados, a amostra fornecida é suficiente para apoiar a hipótese do pesquisador, considerando um nível de significância de 5%?

## Carregar os dados:

x <- db.heart$CPK
x
##  [1] 482 166 253 582 113  57 196  97 588 224 835 249  53 131 582 135  66 104 910
## [20] 260
## Tamanho da amostra:  

length(x)
## [1] 20
## Como n < 30, fazer o uso do teste T pela função t.test

# Uso da função t.test():

# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0, 
#        paired = FALSE, var.equal = FALSE, conf.level = 0.95, ...)

t.test(x = x, alternative = 'less', mu = 450, conf.level = 0.95)
## 
##  One Sample t-test
## 
## data:  x
## t = -2.4753, df = 19, p-value = 0.01145
## alternative hypothesis: true mean is less than 450
## 95 percent confidence interval:
##      -Inf 406.0326
## sample estimates:
## mean of x 
##    304.15
## Valor de T tabelado para hipótese do tipo maior:

qt(0.05, 19)
## [1] -1.729133
## Conclusão:

# Como o valor calculado para T, -2.4753, é menor do que o valor tabelado de T, -1.73, rejeita-se H0. 
# Isto é, ao nível de significância de 5%, não há evidências suficientes para apoiar a hipótese do 
# pesquisador clínico a qual sugere que a média dos valores de CPK dos pacientes com insuficiência 
# cardíaca é superior a 450.

5.4. Teste de Diferença de Médias

5.4.1. Amostras Independentes

O segundo teste paramétrico a ser estudo é o teste de hipóteses para diferença de médias para duas amostras independentes. Este teste, em geral, é utilizado para a comparação de grupos independentes, amostrados de populações diferentes ou da mesma população. Para a construção deste teste, há duas possibilidades: amostras grandes, e amostras pequenas. No primeiro caso, o teste será baseado na estatística Z, e no segundo caso, o nosso teste será baseado na estatística T. As Tabelas 5 e 6 trazem um sumário deste tipo de teste em ambos os casos.

Amostras Grandes Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \mu_1 - \mu_2 \geq D_0\)
\(H_a: \mu_1 - \mu_2 < D_0\)
\(H_0: \mu_1 - \mu_2 \leq D_0\)
\(H_a: \mu_1 - \mu_2 > D_0\)
\(H_0: \mu_1 - \mu_2 = D_0\)
\(H_a: \mu_1 -\mu_2 \neq D_0\)
Estatística do Teste \(z_{calc} = \dfrac{(\bar{x}_1 - \bar{x}_2) - D_0}{\sigma_{\bar{x}_1 - \bar{x}_2}}\)
Região Crítica Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha}\) Rejeita-se \(H_0\) se \(z_{calc} \geq z_{1-\alpha}\) Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha/2}\)
ou se \(z_{calc} \geq z_{1-\alpha/2}\)
Amostras Pequenas Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \mu_1 - \mu_2 \geq D_0\)
\(H_a: \mu_1 - \mu_2 < D_0\)
\(H_0: \mu_1 - \mu_2 \leq D_0\)
\(H_a: \mu_1 - \mu_2 > D_0\)
\(H_0: \mu_1 - \mu_2 = D_0\)
\(H_a: \mu_1 -\mu_2 \neq D_0\)
Estatística do Teste \(t_{calc} = \dfrac{(\bar{x}_1 - \bar{x}_2) - D_0}{s_{\bar{x}_1 - \bar{x}_2}}\)
Região Crítica Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha}\) Rejeita-se \(H_0\) se \(t_{calc} \geq t_{\alpha}\) Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha/2}\)
ou se \(t_{calc} \geq t_{\alpha/2}\)

Observação: No caso de amostras pequenas (\(n_1 < 30\) e \(n_2 < 30\)), o erro-padrão é aproximado com base na combinação das amostras. Isto é, se \(s_1^2\) e \(s_2^2\) são as variâncias amostrais, tem-se que a variância combinada das amostras é descrito por:

\[s_p^2 = \dfrac{(n_1−1)s_1^2+(n_2−1)s_2^2}{n_1 + n_2 − 2}\]

Assim, tem-se que a aproximação do erro-padrão, \(\sigma_{\bar{X}_1 - \bar{X}_2}\), é dado por:

\[\sigma_{\bar{X}_1 - \bar{X}_2} = s_{\bar{X}_1 - \bar{X}_2} = \sqrt{s_p^2\left(\dfrac{1}{n_1} + \dfrac{1}{n_2}\right)}\]

Uma vez que na prática é mais comum o uso do teste t para comparação de médias do que o teste Z, nesta seção, exemplificaremos apenas o teste t para a diferença de médias no R. Neste caso, também fazemos o uso da função t.test disponível no R que é uma função baseada na distribuição t de Student com objetivo de criar intervalos de confiança e testes de hipóteses para problemas com uma ou duas amostras quando o desvio-padrão/variância populacional é desconhecido. Considere, como exemplo, a seguinte situação:

Hoekema et. al (2003) estudaram a morfologia craniofacial de 20 pacientes do sexo masculino com síndrome da apneia obstrutiva do sono (SAOS). Uma das variáveis de interesse foi o comprimento do ponto mais súpero-anterior do corpo do osso hióide até a horizontal de Frankfurt.

Dados amostrais referentes ao comprimento do ponto mais súpero-anterior do corpo do osso hióide até a horizontal de Frankfurt.
Comprimento do Osso (em mm) (Saudáveis) Comprimento do Osso (em mm) (SAOS) Comprimento do Osso (em mm) (Saudáveis) Comprimento do Osso (em mm) (SAOS)
96.80 105.95 97.00 114.90
100.70 114.90 97.70 114.35
94.55 110.35 97.00 112.25
99.65 123.10 94.55 106.15
109.15 119.30 106.45 102.60
102.75 110.00 94.55 102.40
97.70 98.95 94.05 105.05
92.10 114.20 89.45 112.65
89.50 105.05 98.20 117.70

Perguntas de Interesse:

Com base na amostra na tabela abaixo, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, as duas populações amostradas diferem em relação ao comprimento do osso hióide até a horizontal de Frankfurt?

## Carregar os dados:

setwd('datasets')

db.osso <- read.csv('dataset-16-osso.csv', header = TRUE, sep = ',')

names(db.osso) <- c('Comprimento do Osso (em mm) (Saudáveis)', 'Comprimento do Osso (em mm) (SAOS)', 'Comprimento do Osso (em mm) (Saudáveis)', 'Comprimento do Osso (em mm) (SAOS)')

db.osso
##   Comprimento do Osso (em mm) (Saudáveis) Comprimento do Osso (em mm) (SAOS)
## 1                                   96.80                             105.95
## 2                                  100.70                             114.90
## 3                                   94.55                             110.35
## 4                                   99.65                             123.10
## 5                                  109.15                             119.30
## 6                                  102.75                             110.00
## 7                                   97.70                              98.95
## 8                                   92.10                             114.20
## 9                                   89.50                             105.05
##   Comprimento do Osso (em mm) (Saudáveis) Comprimento do Osso (em mm) (SAOS)
## 1                                   97.00                             114.90
## 2                                   97.70                             114.35
## 3                                   97.00                             112.25
## 4                                   94.55                             106.15
## 5                                  106.45                             102.60
## 6                                   94.55                             102.40
## 7                                   94.05                             105.05
## 8                                   89.45                             112.65
## 9                                   98.20                             117.70
## Teste de Hipóteses

# Uso da função t.test():

# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0, 
#        paired = FALSE, var.equal = FALSE, conf.level = 0.95, ...)

saud <- c(db.osso[,1], db.osso[,3])
SAOS <- c(db.osso[,2], db.osso[,4])

t.test(x = saud, y = SAOS, alternative = 'two.sided', mu = 0, conf.level = 0.95, var.equal = TRUE)
## 
##  Two Sample t-test
## 
## data:  saud and SAOS
## t = -6.7365, df = 34, p-value = 9.68e-08
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -17.211083  -9.233362
## sample estimates:
## mean of x mean of y 
##   97.3250  110.5472
## Valor de T tabelado para hipótese:

GL <- 18 + 18 - 2  # Graus de Liberdade
  
qt(0.025, GL)
## [1] -2.032245
## Conclusão:

# Como o valor calculado para T, -6.7365, é menor do que o valor tabelado de T, -2.032, rejeita-se H0. 
# Isto é, ao nível de significância de 5%, evidência suficiente para concluirmos que, ao nível de 
# significância de 5%, as duas populações amostradas diferem em relação ao comprimento do osso hióide 
# até a horizontal de Frankfurt.

5.4.2. Amostras Pareadas

O nosso último teste paramétrico a ser estudo é o teste de hipóteses para diferença de médias para duas amostras pareadas. Este teste, em geral, é utilizado para comprovar se determinado paciente teve melhora após a administração do medicamento, ou tratamento. Para a construção deste teste, há duas possibilidades: amostras grandes, e amostras pequenas. No primeiro caso, o teste será baseado na estatística Z, e no segundo caso, o nosso teste será baseado na estatística T. As Tabelas 8 e 10 trazem um sumário deste tipo de teste em ambos os casos.

Amostras Grandes Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \mu_{antes} - \mu_{depois} \geq D_0\)
\(H_a: \mu_{antes} - \mu_{depois} < D_0\)
\(H_0: \mu_{antes} - \mu_{depois} \leq D_0\)
\(H_a: \mu_{antes} - \mu_{depois} > D_0\)
\(H_0: \mu_{antes} - \mu_{depois} = D_0\)
\(H_a: \mu_{antes} - \mu_{depois} \neq D_0\)
Estatística do Teste \(z_{calc} = \dfrac{\bar{d} - D_0}{s_d/\sqrt{n_d}}\)
Região Crítica Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha}\) Rejeita-se \(H_0\) se \(z_{calc} \geq z_{1-\alpha}\) Rejeita-se \(H_0\) se \(z_{calc} \leq -z_{1-\alpha/2}\)
ou se \(z_{calc} \geq z_{1-\alpha/2}\)
Amostras Pequenas Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \mu_{antes} - \mu_{depois} \geq D_0\)
\(H_a: \mu_{antes} - \mu_{depois} < D_0\)
\(H_0: \mu_{antes} - \mu_{depois} \leq D_0\)
\(H_a: \mu_{antes} - \mu_{depois} > D_0\)
\(H_0: \mu_{antes} - \mu_{depois} = D_0\)
\(H_a: \mu_{antes} - \mu_{depois} \neq D_0\)
Estatística do Teste \(t_{calc} = \dfrac{\bar{d} - D_0}{s_d/\sqrt{n_d}}\)
Região Crítica Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha}\) Rejeita-se \(H_0\) se \(t_{calc} \geq t_{\alpha}\) Rejeita-se \(H_0\) se \(t_{calc} \leq -t_{\alpha/2}\)
ou se \(t_{calc} \geq t_{\alpha/2}\)

Uma vez que na prática é mais comum o uso do teste t para comparação de médias do que o teste Z, nesta seção, exemplificaremos apenas o teste t para a diferença de médias de amostras pareadas no R. Neste caso, também fazemos o uso da função t.test disponível no R que é uma função baseada na distribuição t de Student com objetivo de criar intervalos de confiança e testes de hipóteses para problemas com uma ou duas amostras quando o desvio-padrão/variância populacional é desconhecido. No entanto, há uma sutil diferença do uso em relação ao caso das amostras independentes. Agora é necessário o uso do argumento paired = TRUE para indicar que é uma amostra pareada. Como exemplo, a seguinte situação:

Procellini et. al (2003) investigaram o efeito na contagem de células T-CD4 após administração de interleucina intermitente (IL-2) em adição à terapia antirretroviral altamente ativa (HAART). Na tabela abaixo são exibidas as contagens de células T-CD4 antes e depois da terapia.

Dados amostrais referentes as contagens de células T-CD4 antes e depois da terapia HAART com IL-2.
ID do Paciente Células T-CD4 (Início) Células T-CD4 (HAART)
1 173 257
2 58 108
3 103 315
4 181 362
5 105 141
6 301 549
7 169 369

Perguntas de Interesse:

Com base nessa amostra, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, há uma alteração significativa na contagem de células T-CD4 após o uso da terapia HAART com IL-2?

## Carregar os dados:

setwd('datasets')

db.haart <- read.csv('dataset-15-haart.csv', header = TRUE, sep = ',')

names(db.haart) <- c('ID do Paciente', 'Células T-CD4 (Início)', 'Células T-CD4 (HAART)')
db.haart
##   ID do Paciente Células T-CD4 (Início) Células T-CD4 (HAART)
## 1              1                    173                   257
## 2              2                     58                   108
## 3              3                    103                   315
## 4              4                    181                   362
## 5              5                    105                   141
## 6              6                    301                   549
## 7              7                    169                   369
## Teste de Hipóteses

# Uso da função t.test():

# t.test(x, y = NULL, alternative = c("two.sided", "less", "greater"), mu = 0, 
#        paired = FALSE, var.equal = FALSE, conf.level = 0.95, ...)

t.test(x = db.haart[,2], y = db.haart[,3], alternative = 'two.sided', mu = 0, conf.level = 0.95, paired = TRUE)
## 
##  Paired t-test
## 
## data:  db.haart[, 2] and db.haart[, 3]
## t = -4.46, df = 6, p-value = 0.004283
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
##  -223.66696  -65.19018
## sample estimates:
## mean difference 
##       -144.4286
## Valor de T tabelado para hipótese:

GL <- 7 - 1  # Graus de Liberdade
  
qt(0.025, GL)
## [1] -2.446912
## Conclusão:

# Como o valor calculado para T, -4.46, é menor do que o valor tabelado de T, -2.447, rejeita-se H0. 
# Isto é, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, há uma 
# alteração significativa na contagem de células T-CD4 após o uso da terapia HAART com IL-2.

5.5. Testes Qui-Quadrado

5.5.1. Teste \(\chi^2\) de Aderência

Considere a seguinte tabela de frequências, com \(k\geq 2\) categorias:

Categorias Frequência Observada Frequência Esperada
1 \(O_1\) \(E_1\)
2 \(O_2\) \(E_2\)
\(\ldots\) \(\ldots\) \(\ldots\)
k \(O_k\) \(E_k\)
Total \(n\) \(n\)

Em que \(O_i\) é o total de indivíduos observado, e \(E_i\) é o total de indivíduos esperados obtido por \(E_i=n\cdot p_{0i}\), em que \(p_{0i}\) é a probabilidade especificada pelo modelo probabilístico de interesse. Assim, com base nessa tabela, o teste \(\chi^2\) de aderência tem por objetivo testar a adequabilidade de um modelo probabilístico a um conjunto de dados observados, isto é, testar se a frequência observada é igual ou não a frequência esperada pelo modelo em cada uma das categorias. Então, da comparação de frequências, pode-se concluir que: se elas forem grandes, a \(H_0\) deverá ser rejeitada em favor da \(H_a\), mas se elas forem pequenas, a \(H_0\) não será rejeitada e as diferenças serão atribuíveis de acordo com o modelo utilizado.

Teste Qui-Quadrado Condições
Hipótese Seja \(p_i\) a probabilidade associada à categoria \(i\), então \(H_0: p_i = p_{0i}\) vs. \(H_a:\) Ao menos uma probabilidade é diferente.
Estatística do Teste \(\chi_{calc}^2 = \sum_{i=1}^{n}\dfrac{(O_i - E_i)^2}{E_i}\)
Região Crítica Com base no modelo qui-quadrado com \(k-1\) graus de liberdade, rejeita-se \(H_0\) se \(\chi_{calc}^2 \geq \chi_{tab}^2\)

No R, este teste pode ser realizado por meio da função chisq.test em que podemos declarar as probabilidades do modelo desejado pelo argumento p para testar a aderência de um determinado modelo de probabilidade. Para exemplificar o teste, considere a seguinte situação-problema:

O ácido úrico está entre as substâncias naturalmente produzidas pelo organismo. Ele surge como resultado da quebra das moléculas de purina – proteína contida em muitos alimentos – por ação de uma enzima chamada xantina oxidase. Os níveis de ácido úrico no sangue sobem, em geral, porque o paciente está eliminando pouco pela urina ou por interferência do uso de certos medicamentos. Neste contexto, na tabela abaixo, é apresentado a distribuição das concentrações de ácido úrico tomadas em 250 pacientes.

Ácido Úrico Frequência Observada
< 1 1
1 até 1,99 5
2 até 2,99 15
3 até 3,99 24
4 até 4,99 43
5 até 5,99 50
6 até 6,99 45
7 até 7,99 30
8 até 8,99 22
9 ou mais 15
Total 250

Pergunta de Interesse:

O modelo normal com média \((\mu)\) igual à 5,74 e desvio-padrão \((\sigma)\) igual à 2,01 é adequado para explicar as concentrações de ácido úrico dos pacientes, ao nível de significância de 1%?

## Definir o vetor com as frequências observadas:

f_obs <- c(1,5,15,24,43,50,45,30,22,15)

## Definir o vetor com as probabilidades para cada categoria:

p1  <- pnorm(1, 5.74, 2.01)
p2  <- pnorm(1.99, 5.74, 2.01) - pnorm(1, 5.74, 2.01)
p3  <- pnorm(2.99, 5.74, 2.01) - pnorm(2, 5.74, 2.01)
p4  <- pnorm(3.99, 5.74, 2.01) - pnorm(3, 5.74, 2.01)
p5  <- pnorm(4.99, 5.74, 2.01) - pnorm(4, 5.74, 2.01)
p6  <- pnorm(5.99, 5.74, 2.01) - pnorm(5, 5.74, 2.01)
p7  <- pnorm(6.99, 5.74, 2.01) - pnorm(6, 5.74, 2.01)
p8  <- pnorm(7.99, 5.74, 2.01) - pnorm(7, 5.74, 2.01)
p9  <- pnorm(8.99, 5.74, 2.01) - pnorm(8, 5.74, 2.01)
p10 <- 1 - pnorm(9, 5.74, 2.01)

probs <- round(c(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10),3) + 0.001

## Teste de Hipóteses

# Uso da função chisq.test():

# chisq.test(x, y = NULL, correct = TRUE, p = rep(1/length(x), length(x)), 
#            rescale.p = FALSE, simulate.p.value = FALSE, B = 2000)

chisq.test(x = f_obs, p = probs)
## Warning in chisq.test(x = f_obs, p = probs): Chi-squared approximation may be
## incorrect
## 
##  Chi-squared test for given probabilities
## 
## data:  f_obs
## X-squared = 2.5755, df = 9, p-value = 0.9788
## Valor de X^2 tabelado para hipótese:

qchisq(0.99, 9)
## [1] 21.66599
## Conclusão:

# Como o valor calculado para X^2, 2.5755, é menor do que o valor tabelado de X^2, 21.66599, não rejeita-se H0. 
# Isto é, o modelo normal com média igual à 5,74 e desvio-padrão igual à 2,01 é adequado para explicar as 
# concentrações de ácido úrico dos pacientes, ao nível de significância de 1%.

5.5.2. Teste \(\chi^2\) de Independência

Seguimos agora para a segunda categoria de testes não-paramétricos, os testes para duas amostras independentes. Nessa categoria, o primeiro teste que temos é o teste \(\chi^2\) de independência. Este teste é utilizado para avaliar a dependência ou independência entre as variáveis sendo consideradas.

Para realizar este teste, as variáveis devem estar tabuladas em forma de tabelas de contingência de tamanho \(“k \times n”\), em que “k” representa o nº de linhas e “n” o de colunas. Um exemplo de tabela de contingência para o teste 𝜒^2 de independência é ilustrada na tabela abaixo.

Categoria Y Total
Categoria X \(Y_1\) \(\ldots\) \(Y_s\)
\(X_1\) \(O_{11}\) \(\ldots\) \(O_{1s}\) \(O_{1j}\)
\(X_1\) \(O_{21}\) \(\ldots\) \(O_{2s}\) \(O_{2j}\)
\(X_1\) \(O_{31}\) \(\ldots\) \(O_{3s}\) \(O_{3j}\)
\(\ldots\) \(\ldots\) \(\ldots\) \(\ldots\) \(\ldots\)
\(X_1\) \(O_{r1}\) \(\ldots\) \(O_{rs}\) \(O_{rj}\)
Total \(O_{j1}\) \(\ldots\) \(O_{js}\) \(n\)

Neste caso, as frequências observadas são determinadas pela seguinte expressão:

\[E_{ij} = \dfrac{O_{ij} \cdot O_{ji}}{n}\]

Assim, com base na tabela anterior, o teste \(\chi^2\) de independência tem por objetivo testar se variáveis são independentes ou não com base em um conjunto de dados observados, isto é, testar se a frequência observada é igual ou não a frequência esperada entre as observações das variáveis em cada categoria. Então, da comparação de frequências, pode-se concluir que: se elas forem grandes, a \(H_0\) deverá ser rejeitada em favor da \(H_a\), mas se elas forem pequenas, a \(H_0\) não será rejeitada e as diferenças serão atribuíveis de acordo com o modelo utilizado.

Teste Qui-Quadrado Condições
Hipótese \(H_0\): As variáveis X e Y são independentes vs. \(H_a\): As variáveis X e Y não são independentes
Estatística do Teste \(\chi_{calc}^2 = \sum_{i=1}^{n}\dfrac{(O_{ij} - E_{ij})^2}{E_{ij}}\)
Região Crítica Com base no modelo qui-quadrado com \((r-1)(s-1)\) graus de liberdade, rejeita-se \(H_0\) se \(\chi_{calc}^2 \geq \chi_{tab}^2\)

No R, este teste pode ser realizado também por meio da função chisq.test, só que agora iremos criar uma matriz referente a tabela de contingência dos dados, e usando o argumento correct = FALSE (este argumento deve ser utilizado como TRUE apenas se a correção de Yates for necessária). Para exemplificar o teste, considere a seguinte situação-problema:

A infecção do sítio cirúrgico esternal (ISC) após cirurgia de revascularização miocárdica é uma complicação que aumenta a morbidade do paciente. Neste aspecto, Segal e Anderson (2002) realizaram um estudo que examinou dois tipos de preparação pré-operatória da pele antes da realização de cirurgia cardíaca.

Tipo de Preparação
Dados amostrais referente aos dois tipos de preparação pré-operatória da pele antes da realização de cirurgia cardíaca.
Grupo de Preparação Iodo Aquoso Iodo Insolúvel Total
Infectado 14 4 18
Não-Infectado 94 97 191
Total 108 101 209

Pergunta de Interesse:

Esses dados fornecem evidência suficiente, ao nível de significância de 5%, para justificar a conclusão de que o tipo de preparação da pele e a infecção estão relacionados?

## Definir o vetor com as frequências observadas:

f_obs <- matrix(c(14,94,4,97), ncol = 2)

## Teste de Hipóteses

# Uso da função chisq.test():

# chisq.test(x, y = NULL, correct = TRUE, p = rep(1/length(x), length(x)), 
#            rescale.p = FALSE, simulate.p.value = FALSE, B = 2000)

chisq.test(x = f_obs, p = probs)
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  f_obs
## X-squared = 4.2913, df = 1, p-value = 0.03831
## Valor de X^2 tabelado para hipótese:

qchisq(0.95, 1)
## [1] 3.841459
## Conclusão:

# Como o valor calculado para X^2, 5.3743, é maior do que o valor tabelado de X^2, 3.8414, rejeita-se H0. 
# Isto é, ao nível de significância de 5%, pode-se concluir que não há evidências suficientes para afirmar
# que há uma associação entre o tipo de preparação da pele e a infecção.

5.6. Teste de Mantel-Haenzel

O segundo teste, o teste de Mantel-Haenszel, foi desenvolvido por N. Mantel e W. Haenszel no ano de 1959. Tal teste é uma técnica que gera uma estimativa de uma associação entre uma doença e um fator de risco após o ajuste ou tendo em conta a confusão. Neste teste, os dados a serem analisados consistem de várias tabelas de contingência (Tabela 2), em vez de apenas uma. Se for apropriado, o teste fornecerá um meio de calcularmos uma estimativa por ponto ou um intervalo de confiança para o odds ratio da população global. E, além disso, nos permitirá testar a hipótese nula de não-associação entre a exposição e a doença.

Ocorrência da Doença
Tabela 2: I-ésima tabela de contingência genérica para o teste de Mantel-Haenzel para associação entre duas amostras.
Fator de Risco Presente Ausente Total
Presente Ai Bi Ai + Bi
Ausente Ci Di Ci + Di
Total Ai + Ci Bi + Di Ni

O teste de Mantel-Haenzel, dois pressupostos devem ser satisfeitos: (1) as observações são independentes; (2) todas as observações devem ser identicamente distribuídas. Cumpridos esses pressupostos, podemos então prosseguir com o teste de Mantel-Haenzel. Para facilitar nosso entendimento deste teste, ele será dividido em etapas. A primeira etapa do teste de Mantel-Haenszel, então, é fornecer uma estimativa pontual para o estimador da odds ratio do mesmo modo que foi feito para as distribuições amostrais. Neste caso, com base na tabela de contingência defina na Tabela 2, tem-se que o estimador da odds ratio é dado por:

\[\widehat{OM}_{MH} = \dfrac{\sum_{i=1}^k\frac{A_i D_i}{N_i}}{\sum_{i=1}^k\frac{B_i C_i}{N_i}}\]

A partir desse estimador, podemos definir um intervalo de \(100\cdot (1 - \alpha)\%\) de confiança, em torno de \(\widehat{OM}_{MH}\), para o método de Mantel-Haenszel. Porém para estimar o intervalo de confiança, precisamos de um estimador da variância em torno de \(\widehat{OM}_{MH}\). Então, a partir da Tabela 2, tem-se:

\[Var(\widehat{OM}_{MH}) = \dfrac{\sum_{i=1}^k A_i D_i\left(\frac{A_i + D_i}{N_i}\right)}{2\left(\sum_{i=1}^k\frac{A_i D_i}{N_i}\right)^2} + \dfrac{\sum_{i=1}^k A_i D_i(B_i + C_i) + B_i C_i(A_i + D_i)}{2N_{i}^2\sum_{i=1}^k\frac{A_i D_i}{N_i} \sum_{i=1}^k\frac{B_i C_i}{N_i}} + \dfrac{\sum_{i=1}^k B_i C_i\left(\frac{B_i + C_i}{N_i}\right)}{2\left(\sum_{i=1}^k\frac{B_i C_i}{N_i}\right)^2}\]

Assim como feito na distribuição amostral, o nosso estimador precisa ser corrigido de acordo com o tamanho amostral. Neste caso, então, a correção do estimador é descrita por:

\[Var(\ln(\widehat{OM}_{MH})) = \dfrac{Var(\widehat{OM}_{MH})}{(\widehat{OM}_{MH})^2}\]

E, assim, o intervalo de 95% confiança para a odds ratio, em torno de \(\widehat{OM}_{MH}\), é descrito por:

\[IC_{\widehat{OM}_{MH}} = e^{\ln(\widehat{OM}_{MH})} \pm 1,96 \cdot \sqrt{Var(\ln(\widehat{OM}_{MH}))}\]

A a segunda etapa do teste de Mantel-Haenszel consiste em combinar a informação de duas ou mais tabelas de contingências afim de testar se o odds ratio é igual a 1. Assim, se o odds ratio for igual a 1 significa que não há associação entre a doença e o fator de risco. Neste caso, então, definimos as hipóteses:

\[ H_0: \widehat{OM}_{MH} = 1\] \[vs.\] \[H_a: \widehat{OM}_{MH} \neq 1\]

Agora, para cada combinação das tabelas de contingências, pode-se calcular o estimador do valor esperado, da célula superior esquerda da seguinte forma:

\[\hat{E}_i = \dfrac{(A_i+B_i)(A_i+C_i)}{N_i}\]

E o estimador da variância, neste caso, pode ser calculado como:

\[\widehat{Var}_i \dfrac{(A_i+B_i)(A_i+C_i)(B_i + D_i)(C_i + D_i)}{N_{i}^2(N_i - 1)}\]

Com base nesses resultados, podemos então prosseguir com a estatística do teste de Mantel-Haenzel. Tal estatística será baseada na estatística \(\chi^2\) e, neste caso, será definida como:

\[\chi^2_{MH} = \dfrac{\left(\sum_{i=1}^{k} A_i - \sum_{i=1}^{k} \hat{E}_i\right)^2}{\sum_{i=1}^{k}\widehat{Var}_i}\]

Assim, rejeitamos a hipótese nula se o valor de \(\chi^2_{MH}\) for igual ou maior do que o valor crítico da estatística do teste, que é o valor do qui-quadrado tabelado para 1 grau de liberdade e o nível de significância escolhido.

Teste Mantel-Haenszel Condições
Hipótese \(H_0\): Não há associação entre o fator de risco e a doença vs. \(H_a\): Há associação entre o fator de risco e a doença
Estatística do Teste \(\chi^2_{MH} = \dfrac{\left(\sum_{i=1}^{k} A_i - \sum_{i=1}^{k} \hat{E}_i\right)^2}{\sum_{i=1}^{k}\widehat{Var}_i}\)
Região Crítica Com base no modelo qui-quadrado com 1 grau de liberdade, rejeita-se \(H_0\) se \(\chi_{MH}^2 \geq \chi_{tab}^2\)

No R, este teste pode ser realizado por meio da função mantelhaen.test, sendo necessário criar um array referente a tabela de contingência dos dados, e usando o argumento correct = FALSE (este argumento deve ser utilizado como TRUE apenas se a correção de Yates for necessária). Para exemplificar o teste, considere a seguinte situação-problema:

Em seu estudo de LaMont et. al (2002), coletaram dados sobre doença arterial coronariana obstrutiva (OCAD), hipertensão e idade entre os pacientes identificados por um teste de estresse em esteira como estando em risco. Os dados do estudo estão dispostos na tabela abaixo.

Estrato 1: Abaixo de 55 anos
OCAD
Pacientes estratificados por idade e classificados por status relativo à hipertensão (fator de risco) e OCAD (variável caso/não caso).
Hipertensão Casos Não-Casos Total
Presente 21 11 32
Ausente 16 6 22
Total 37 17 54
Estrato 2: Acima de 55 anos
OCAD
Hipertensão Casos Não-Casos Total
Presente 50 14 64
Ausente 18 6 24
Total 68 20 88

Pergunta de Interesse:

Esses dados fornecem evidência suficiente, ao nível de significância de 5%, para justificar a conclusão de que não existe uma associação entre a presença de hipertensão e a ocorrência de OCAD nos pacientes com idade abaixo ou acima de 55 anos?

## Definir o vetor com as frequências observadas:

data_MH <- array(c(21,16,11,6,50,18,14,6), 
                 dim = c(2,2,2), 
                 dimnames = list(Hipertensao = c("Presente","Ausente"),
                                 OCAD = c("Casos","Nao_Casos"), 
                                 Idade = c("< 55_Anos", "> 55_Anos")))

## Teste de Hipóteses

# Uso da função chisq.test():

# mantelhaen.test(x, y = NULL, z = NULL, alternative = c("two.sided", "less", "greater"),
#                correct = TRUE, exact = FALSE, conf.level = 0.95)

mantelhaen.test(data_MH, correct = FALSE)
## 
##  Mantel-Haenszel chi-squared test without continuity correction
## 
## data:  data_MH
## Mantel-Haenszel X-squared = 0.024265, df = 1, p-value = 0.8762
## alternative hypothesis: true common odds ratio is not equal to 1
## 95 percent confidence interval:
##  0.4184851 2.1018264
## sample estimates:
## common odds ratio 
##         0.9378609
## Valor de X^2 tabelado para hipótese:

qchisq(0.95, 1)
## [1] 3.841459
## Conclusão:

# Como o valor calculado para X^2, 0.024265, é menor do que o valor tabelado de X^2, 3.8414, não rejeita-se H0. 
# Isto é, ao nível de significância de 5%, pode-se concluir que não há evidências suficientes para afirmar
# que uma associação entre a presença de hipertensão e a ocorrência de OCAD nos pacientes, de acordo com a idade.

5.7. Teste de Mann-Whitney

Este teste, em geral, é utilizado para comprovar se dois grupos independentes foram ou não extraídos da mesma população, sendo o análogo não-paramétrico do teste paramétrico t de Student. Para a construção deste teste, considere \(n_1\) = número de casos no menor dos dois grupos independentes e \(n_2\) = número de casos no maior grupo. Primeiramente, combinam-se as observações ou escores de ambos os grupos, relacionando-os por ordem ascendente.

Nessa ordenação ascendente, consideram-se os valores algébricos do grupo \(n = n_1 + n_2\), isto é, os postos mais baixos são atribuídos aos maiores valores (negativos se houver). Agora, focaliza-se agora um dos grupos, por exemplo, o grupo que apresenta \(n_1\) casos. Logo, a estatística teste, U, será dada pelo número de vezes que um escore no grupo com \(n_2\) casos precede um escore no grupo com \(n_1\) casos no grupo ordenado formado por \(n = n_1 + n_2\) casos. Aqui, consideraremos o cálculo da estatística apenas para o caso em que \(n \leq 20\) (amostras pequenas) e com hipóteses do tipo bilateral.

Neste caso, a estatística do teste de Mann-Whitney pode escrita, de modo geral, pelas seguintes expressões:

\[U_1 = n_1 n_2 + \dfrac{n_1(n_1 + 1)}{2} − R_1\]

e,

\[U_2 = n_1 n_2 + \dfrac{n_2(n_2 + 1)}{2} − R_2\]

em que \(R_i\) é a soma dos postos grupo \(i = 1, 2\). A regra de decisão, assim como nos testes paramétricos e qui-quadrado, também será baseada em uma tabela de valores críticos, chamada de tabela U.

Tabela U: Valores críticos de U assumindo um teste bilateral com 5% de significância.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
2 . . . 0 0 0 0 1 1 1 1 1 2 2 2 2
3 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8
4 1 2 3 4 4 5 6 7 8 9 10 11 11 12 13 14
5 2 3 5 6 7 8 9 11 12 13 14 15 17 18 19 20
6 . 5 6 8 10 11 13 14 16 17 19 21 22 24 25 27
7 . . 8 10 12 14 16 18 20 22 24 26 28 30 32 34
8 . . . 13 15 17 19 22 24 26 29 31 34 36 38 41
9 . . . . 17 20 23 26 28 31 34 37 39 42 45 48
10 . . . . . 23 26 29 33 36 39 42 45 48 52 55
11 . . . . . . 30 33 37 40 44 47 51 55 58 62
12 . . . . . . . 37 41 45 49 53 57 61 65 69
13 . . . . . . . . 45 50 54 59 63 67 72 76
14 . . . . . . . . . 55 59 64 69 74 78 83
15 . . . . . . . . . . 64 70 75 80 85 90
16 . . . . . . . . . . . 75 81 86 92 98
17 . . . . . . . . . . . . 87 93 99 105
18 . . . . . . . . . . . . . 99 106 112
19 . . . . . . . . . . . . . . 113 119
20 . . . . . . . . . . . . . . . 127
Teste de Mann-Whitney Condições
Hipótese \(H_0\): Os grupos apresentam a mesma distribuição vs. \(H_a\): Os grupos não apresentam a mesma distribuição
Estatística do Teste \(U_{calc} = \min(U_1, U_2)\)
Região Crítica Com base no nível de significância de 5%, rejeita-se \(H_0\) se \(U_{calc} \leq U_{tab}\)

Para trabalhar com este teste no R, fazemos o uso da função wilcox.test. Por ser um teste análogo ao teste paramétrico t de Student para diferença de médias, essa função, no R, tem seu funcionamento examente igual à função t.test para amostras independentes. Assim, para exemplificar como ela funciona, vamos considerar o seguinte exemplo:

A tianeptina é um fármaco antidepressivo do grupo dos tricíclicos. Sua ação antidepressiva potencial foi demonstrada em estudos pré-clínicos através de testes em animais. Rocha (1995) relata os resultados de um ensaio clínico aleatorizado, duplo-cego, realizado com o objetivo de comparar a tianeptina com o placebo. O ensaio constituiu em administrar a droga a dois grupos de pacientes, compostos de forma aleatória, e quantificar a depressão em que os valores maiores indicam maior gravidade da depressão. Os escores foram obtido para cada paciente 7, 14, 21, 28 e 42 dias após o início do estudo, e estão dispostos abaixo.

Dados referentes aos escores obtidos para cada paciente 7, 14, 21, 28 e 42 dias após o início do estudo.
Placebo Tianeptina
3 9
4 7
5 5
2 10
6 8
2 8
5 9
10 2
8 7
9 6
10 12

Pergunta de Interesse:

Ao nível de 5% de significância, pode-se concluir que há diferença significativa entre os escores dos medicamentos, indicando que um medicamento possui uma eficácia melhor que o outro no controle da depressão?

## Carregar os dados:

placebo <- c(3,4,5,2,6,2,5,10,8,9,10)
tianeptina <- c(9,7,5,10,8,8,9,2,7,6,12)

## Teste de Hipóteses

# Uso da função wilcox.test():

# wilcox.test(x, y = NULL, alternative = c("two.sided", "less", "greater"),
#            mu = 0, paired = FALSE, exact = NULL, correct = TRUE,
#            conf.int = FALSE, conf.level = 0.95,
#            tol.root = 1e-4, digits.rank = Inf, ...)

wilcox.test(x = placebo, y = tianeptina, alternative = 'two.sided', mu = 0, conf.level = 0.95, paired = TRUE)
## Warning in wilcox.test.default(x = placebo, y = tianeptina, alternative =
## "two.sided", : cannot compute exact p-value with ties
## Warning in wilcox.test.default(x = placebo, y = tianeptina, alternative =
## "two.sided", : cannot compute exact p-value with zeroes
## 
##  Wilcoxon signed rank test with continuity correction
## 
## data:  placebo and tianeptina
## V = 15, p-value = 0.2201
## alternative hypothesis: true location shift is not equal to 0
## Conclusão:

# Como o valor calculado para U, U_calc = 41.5, é maior do que o valor tabelado de U para amostras de 
# tamanho 11, U_tab = 30, não rejeita-se H0. Isto é, ao nível de 5% de significância, pode-se concluir
# que não há diferença significativa entre os escores dos medicamentos, indicando que um medicamento 
# não possui uma eficácia melhor que o outro no controle da depressão.

5.8. Teste de Wilcoxon

O nosso último teste não-paramétrico para duas amostras, no caso pareadas, é o teste de Wilcoxon. Este teste baseia-se nos postos das diferenças entrapares, e é utilizado para comparar as medianas de amostras pareadas, sendo o análogo não-paramétrico do teste t pareado.

Para a construção deste teste, considere que \(X_i\) representa os escores da amostra 1 e \(y_i\) os escores da amostra 2. Primeiramente, são calculadas as diferenças de cada par de escores, \(d_i = X_i–Y_i\). Em seguida, atribuem-se postos às diferenças dos escores em valor absoluto, \(|d_i|\).

A menor diferença em valor absoluto receberá o posto 1, a segunda menor diferença em valor absoluto receberá o posto 2 e assim por diante. Depois, acrescenta-se o sinal das diferenças, \(d_i\), aos postos. Assim, sob a hipótese nula \((H_0)\), a soma dos postos positivos “+” e a soma dos postos negativos “-” devem ser aproximadamente iguais, indicando que não existe diferença entre as medianas dos grupos, se as somas não forem aproximadamente iguais pode-se dizer que as medianas dos grupos são diferentes, rejeitando-se \(H_0\).

Os empates podem ocorrer de duas maneiras: quando a diferença dos escores X e Y for zero \((|d_i| = 0)\), retira-se o par da análise. Quando houver diferenças em valor absoluto, \(|d_i|\), iguais, atribuem-se à essas diferenças a média dos postos que elas receberiam se não fossem empatadas e depois acrescentam-se aos postos os sinais das diferenças. Por fim, as hipóteses do teste são definidas como:

\[H_0: \sum p_i^{+}= \sum p_i^{−}\] \[vs.\] \[H_a: \sum p_i^{+} \neq (\text{ou } >, \text{ou } <) \sum p_i^{−}\]

Naturalmente, este teste pode ser aplicado tanto à pequenas amostras, quanto à grandes amostras. No entanto, nosso foco será apenas para o caso das pequenas amostras \((n\leq 20)\). Então, para \(n\leq 20\), tem-se que a estatística do teste é dada por:

\[𝑊 = \sum_{i=1}^{m} p_i^{+}\] em que \(m\) é o número de \(d_i^{+}\) e \(p_i^{+}\) é o posto (ordem) de \(|d_i|\) positivo. A regra de decisão, será baseada em uma tabela de valores críticos (Tabela W).

Bilateral
Unilateral
Tabela W: Valores críticos de W assumindo um teste bilateral/Unilateral com 1% e 5% de significância.
n 0.05 0.01 0.05 0.01
5 . . 0.00 .
6 0 . 2.00 .
7 2 . 3.00 0
8 3 0 5.00 1
9 5 1 8.00 3
10 8 3 10.00 5
11 10 5 13.00 7
12 13 7 17.00 9
13 17 9 21.00 12
14 21 12 25.00 15
15 25 15 30.00 19
16 29 19 35.00 23
17 34 23 41.00 27
18 40 27 47.00 32
19 46 32 53.00 37
20 52 37 60.00 43
21 58 42 67.00 49
22 65 48 75.00 55
23 73 54 83.00 62
24 81 61 91.00 69
25 89 68 100.00 76
Teste de Wilcoxon Teste Unilateral (Superior) Teste Unilateral (Inferior) Teste Bilateral
Hipótese \(H_0: \sum p_i^{+}= \sum p_i^{−}\)
\(H_a: \sum p_i^{+}> \sum p_i^{−}\)
\(H_0: \sum p_i^{+}= \sum p_i^{−}\)
\(H_a: \sum p_i^{+}< \sum p_i^{−}\)
\(H_0: \sum p_i^{+}= \sum p_i^{−}\)
\(H_a: \sum p_i^{+}\neq \sum p_i^{−}\)
Estatística do Teste \(W_{calc} = \sum_{i=1}^{m} p_i^{+}\)
Região Crítica Rejeita-se \(H_0\) se \(W_{calc} \geq W_{\alpha}\) Rejeita-se \(H_0\) se \(W_{calc} \geq W_{\alpha}\) Rejeita-se \(H_0\) se \(W_{calc} \geq W_{\alpha/2}\)

Para trabalhar com este teste no R, fazemos o uso da função wilcox.test. Por ser um teste análogo ao teste paramétrico t de Student para diferença de médias, essa função, no R, tem seu funcionamento examente igual à função t.test para amostras independentes. Assim, para exemplificar como ela funciona, vamos considerar o seguinte exemplo:

Procellini et. al (2003) investigaram o efeito na contagem de células T-CD4 após administração de interleucina intermitente (IL-2) em adição à terapia antirretroviral altamente ativa (HAART). Na tabela abaixo são exibidas as contagens de células T-CD4 antes e depois da terapia.

Dados amostrais referentes as contagens de células T-CD4 antes e depois da terapia HAART com IL-2.
ID do Paciente Células T-CD4 (Início) Células T-CD4 (HAART)
1 173 257
2 58 108
3 103 315
4 181 362
5 105 141
6 301 549
7 169 369

Perguntas de Interesse:

Com base nessa amostra, existe evidência suficiente para concluirmos que, ao nível de significância de 5%, há uma alteração significativa na contagem de células T-CD4 após o uso da terapia HAART com IL-2?

## Carregar os dados:

setwd('datasets')

db.haart <- read.csv('dataset-15-haart.csv', header = TRUE, sep = ',')

names(db.haart) <- c('ID do Paciente', 'Células T-CD4 (Início)', 'Células T-CD4 (HAART)')
db.haart
##   ID do Paciente Células T-CD4 (Início) Células T-CD4 (HAART)
## 1              1                    173                   257
## 2              2                     58                   108
## 3              3                    103                   315
## 4              4                    181                   362
## 5              5                    105                   141
## 6              6                    301                   549
## 7              7                    169                   369
## Teste de Hipóteses

# Uso da função t.test():

# wilcox.test(x, y = NULL, alternative = c("two.sided", "less", "greater"),
#            mu = 0, paired = FALSE, exact = NULL, correct = TRUE,
#            conf.int = FALSE, conf.level = 0.95,
#            tol.root = 1e-4, digits.rank = Inf, ...)

wilcox.test(x = db.haart[,2], y = db.haart[,3], alternative = 'two.sided', mu = 0, conf.level = 0.95, paired = TRUE)
## 
##  Wilcoxon signed rank exact test
## 
## data:  db.haart[, 2] and db.haart[, 3]
## V = 0, p-value = 0.01563
## alternative hypothesis: true location shift is not equal to 0
## Conclusão:

# Como o valor calculado para W, W_calc = 0, é menor do que o valor tabelado de W para amostras de 
# tamanho 7, W_tab = 2, rejeita-se H0. Isto é, existe evidência suficiente para concluirmos que, 
# ao nível de significância de 5%, há uma alteração significativa na contagem de células T-CD4 
# após o uso da terapia HAART com IL-2.

6. Análise de Regressão

6.1. Regressão Linear

A regressão linear é usada para prever o valor de uma variável de resultado Y com base em uma ou mais variáveis de previsão de entrada X. O objetivo é estabelecer uma relação linear (uma fórmula matemática) entre a(s) variável(is) de previsão e a variável de resposta, de modo que , podemos usar esta fórmula para estimar o valor da resposta Y, quando apenas os valores dos preditores (Xs) são conhecidos.

O objetivo da regressão linear é modelar uma variável contínua Y como uma função matemática de uma ou mais variáveis X, para que possamos usar esse modelo de regressão para prever o Y quando apenas o X é conhecido. Esta equação matemática pode ser generalizada da seguinte forma:

\[Y = \beta_0 + \beta_1 X + \epsilon\]

onde, \(\beta_0\) (intercepto) \(\beta_1\) (inclinação) são os coeficientes de regressão, e \(\epsilon\) é o termo de erro, a parte de Y que o modelo de regressão não consegue explicar.

No geral, quando trabalhamos com modelos de regressão linear, algumas suposições a respeito dos dados são necessárias, tais como:

  • Linearidade dos dados: A relação entre o preditor (x) e o resultado (y) é assumida como linear.
  • Normalidade dos resíduos: Os erros residuais são considerados normalmente distribuídos.
  • Homogeneidade da variância dos resíduos: Supõe-se que os resíduos tenham uma variância constante (homocedasticidade)
  • Independência dos termos de erro dos resíduos.

Então, para exemplificar como uma análise de regressão linear funciona, em especial, no R, usaremos o conjunto de dados de cars que está disponível no R. Esta base de dados consiste em 50 observações, comtemplando as variáveis distância percorrida (dist) e velocidade (speed). Para visualiza-lá, basta usar o comando head() do R:

head(cars)
##   speed dist
## 1     4    2
## 2     4   10
## 3     7    4
## 4     7   22
## 5     8   16
## 6     9   10

Antes de começarmos a construir o modelo de regressão, é uma boa prática analisar e entender as variáveis. Para esse fim, partiremos de duas análises importantes: a análise gráfica e o estudo da correlação entre as variáveis.

6.2. Análise Gráfica

O objetivo aqui é construir um modelo de regressão simples que possamos usar para prever distância percorrida (dist) estabelecendo uma relação linear estatisticamente significativa com velocidade (speed). Mas antes de pular para a sintaxe do modelo, vamos tentar entender essas variáveis graficamente. Normalmente, para cada uma das variáveis independentes (preditoras), os seguintes gráficos são desenhados para visualizar o seguinte comportamento:

  • Gráfico de dispersão: Visualiza a relação linear entre o preditor e a resposta.
  • Box-plot: Para detectar quaisquer observações atípicas na variável. Ter outliers em seu preditor pode afetar drasticamente as previsões, pois podem afetar facilmente a direção/inclinação da linha de melhor ajuste.
  • Gráfico de densidade: Para ver a distribuição da variável preditora. Idealmente, é preferível uma distribuição próxima à normal (uma curva em forma de sino), sem ser assimétrica para a esquerda ou para a direita. Vamos ver como fazer cada um deles.

6.2.1. Gráfico de Dispersão

Os gráficos de dispersão podem ajudar a visualizar quaisquer relações lineares entre a variável dependente (resposta) e as variáveis independentes (preditoras). Idealmente, se você tiver várias variáveis preditoras, um gráfico de dispersão é desenhado para cada uma delas em relação à resposta. Embora o gráfico de dispersão possa ser feito com a função plot(), aqui utilizaremos a função scatter.smooth() que traz a linha de suavização dos dados.

## Uso da função scatter.smooth():

# scatter.smooth(x, y = NULL, span = 2/3, degree = 1,
#               family = c("symmetric", "gaussian"),
#               xlab = NULL, ylab = NULL,
#               ylim = range(y, pred$y, na.rm = TRUE),
#               evaluation = 50, ..., lpars = list())

scatter.smooth(x=cars$speed, y=cars$dist, main="Dist ~ Speed")

O gráfico de dispersão junto com a linha de suavização acima sugere uma relação linearmente crescente entre as variáveis dist e speed. Isso é bom, porque uma das suposições subjacentes na regressão linear é que a relação entre a resposta e as variáveis preditoras é linear.

6.2.2. Box-Plot

Agora, iremos aos boxplots para cada uma das variáveis: resposta e preditora. Por questões de visualizações, em nosso boxplot, uma função adicional será considerada, a boxplot.stats. Essa função descreve, como legenda em nosso gráfico, as linhas em que se encontram possíveis outliers cujo seu conhecimento é fundamental quando lidamos com modelos de regressão, especialmente os lineares.

par(mfrow=c(1, 2))
boxplot(cars$speed, main="Speed", 
        sub=paste("Outlier rows: ", boxplot.stats(cars$speed)$out))  # box plot para 'speed'
boxplot(cars$dist, main="Distance", 
        sub=paste("Outlier rows: ", boxplot.stats(cars$dist)$out))  # box plot para 'distance'

6.3.3. Gráfico de Densidade

Anteriormente, vimos que o gráfico de densidade é uma versão suavizada do histograma e é usado no mesmo conceito, ou seja, para representar a distribuição de uma variável numérica. E este gráfico é fundamental quando falamos de regressão para ver com mais detalhes como se comportam as nossas variáveis, resposta e preditora, de modo geral. Para nosso estudo aqui, iremos fazer o uso do pacote e1071 para fazer o gráfico de densidade, pois ele traz o cálculo da assimetria dos dados agregado ao gráfico, que torna a descrição do comportamento mais interessante.

## Carregar o pacote

library(e1071)
## 
## Attaching package: 'e1071'
## The following object is masked from 'package:gtools':
## 
##     permutations
## The following objects are masked from 'package:agricolae':
## 
##     kurtosis, skewness
## Fazer o gráfico

par(mfrow=c(1, 2))
plot(density(cars$speed), main="Density Plot: Speed", ylab="Frequency", 
     sub=paste("Skewness:", round(e1071::skewness(cars$speed), 2)))  # density plot para 'speed'
polygon(density(cars$speed), col="red")

plot(density(cars$dist), main="Density Plot: Distance", ylab="Frequency", 
     sub=paste("Skewness:", round(e1071::skewness(cars$dist), 2)))  # density plot para 'dist'
polygon(density(cars$dist), col="red")

6.3. Correlação

Correlação é uma medida estatística que sugere o grau de dependência linear entre duas variáveis, que ocorrem em par, podendo assumir valores entre -1 e 1. Ao calcular a correlação entre as nossas variáveis, pode-se notar que a mesma é positiva e mais próxima de 1 do que de 0, o que indica que há, neste caso, uma relação linear positiva entre as variáveis, conforme visualizamos no gráfico de dispersão anteriormente.

cor(cars$speed, cars$dist)  
## [1] 0.8068949

6.4. Construindo o Modelo Linear

Agora que vimos o relacionamento linear no gráfico de dispersão e calculando a correlação, vamos ver a sintaxe para construir o modelo linear. No R, a função utilizada para a construção de modelos lineares é lm(). A função lm() recebe dois argumentos principais: formula, e data. Em geral, o argumento data recebe como objeto um data.frame e o argumento formula recebe um objeto de classe fórmula. No entanto, a convenção mais comum é simplesmente escrever a fórmula diretamente no lugar do argumento formula, conforme a rotina abaixo.

linearMod <- lm(dist ~ speed, data = cars)
print(linearMod)
## 
## Call:
## lm(formula = dist ~ speed, data = cars)
## 
## Coefficients:
## (Intercept)        speed  
##     -17.579        3.932

Agora que construímos o modelo linear, também estabelecemos a relação entre o preditor e a resposta na forma de uma fórmula matemática para distância (dist) como uma função para velocidade (speed). Assim, para a saída acima, você pode notar que a parte ‘Coeficientes’ tem dois componentes: Intercept: -17,579, speed: 3,932. Estes coeficientes também são chamados de coeficientes beta do nosso modelo. Em outras palavras,

\[dist = Intercept + (\beta_1 \cdot speed) = \beta_0 + \beta_1 speed \] Com os valores betas obtidos, tem-se que

\[dist = −17.579 + 3.932 \cdot speed\]

6.5. Diagnóstico do Modelo Linear

Agora o modelo linear está construído e temos uma fórmula que podemos usar para prever o valor da distância se uma velocidade correspondente for conhecida. A nossa primeira pergunta é: Isso é o suficiente para realmente usar este modelo? A resposta imediata para essa pergunta é NÃO! Antes de usar um modelo de regressão, é necessário garantir que ele seja estatisticamente significativo. Como pode-se garantir isso? Bem, inicialmente, começamos analisando o resumo do modelo por meio da função summary().

summary(linearMod)
## 
## Call:
## lm(formula = dist ~ speed, data = cars)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -29.069  -9.525  -2.272   9.215  43.201 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -17.5791     6.7584  -2.601   0.0123 *  
## speed         3.9324     0.4155   9.464 1.49e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 15.38 on 48 degrees of freedom
## Multiple R-squared:  0.6511, Adjusted R-squared:  0.6438 
## F-statistic: 89.57 on 1 and 48 DF,  p-value: 1.49e-12

6.5.1. P-valor

O resumo estatístico do modelo apresentado acima nos diz várias coisas. Um deles é o p-valor do modelo (última linha inferior) e o p-valor de variáveis preditoras individuais (coluna da extrema direita em ‘Coeficientes’). Os p-valores, em geral, consideram um modelo linear como estatisticamente significativo apenas quando ambos os p-valores forem menores que o nível de significância estatística pré-determinado, que é idealmente 0,05. No R, isso é interpretado visualmente pelas “estrelas” no final da linha. Quanto mais estrelas ao lado do p-valor da variável, mais significativa a variável.

6.5.2. Hipótese Nula e Alternativa

Quando há um p-valor, há uma hipótese nula e uma hipótese alternativa associada a esse valor. No contexto de Regressão Linear, a Hipótese Nula é que os coeficientes associados às variáveis são iguais a zero, isto é, \(\beta_i = 0\). Por outro lado, a hipótese alternativa é que os coeficientes não são iguais a zero (ou seja, existe uma relação entre a variável independente em questão e a variável dependente).

6.5.3. Valor t

Em alternativa ao p-valor, a função summary também nos traz o valor t baseado na distruição t de Student para avaliação das hipóteses de teste, isto é, a significância ou não de um determinado coeficiente. Neste contexto, então, observa-se que um valor t maior indica que é menos provável que o coeficiente não seja igual a zero. Em outras palavras, “quanto maior o valor t, melhor”.

6.5.4. Resíduos do Modelo linear

Os resíduos de um modelo linear são obtidos pela expressão \(r_i = y_i - \hat{y}_i\), em que \(y_i\) é o valor observado nos dados e \(\hat{y}_i\) é o valor da previsão. No R, em vez trabalharmos com essa expressão, trabalhamos diretamente com os métodos gráficos originados dessa expressão que podem ser obtidos de duas formas: pela função plot(), ou pela função autoplot() do pacote ggfortify

## Resíduos pela função plot():

par(mfrow = c(2, 3))
plot(linearMod, which = 1:6)

## Resíduos pela função autoplot():

# Carregar o pacote:

library(ggfortify)
## Warning: package 'ggfortify' was built under R version 4.2.2
# Uso da função autoplot():

# autoplot(object, ...)

autoplot(linearMod)

Os gráficos de diagnóstico mostram os resíduos de quatro maneiras diferentes:

  • Residuals vs Fitted: Usado para verificar as suposições de relacionamento linear. Uma linha horizontal, sem padrões distintos é indício de uma relação linear, o que é bom.

  • Normal Q-Q: Usado para examinar se os resíduos são normalmente distribuídos. É bom que os pontos residuais sigam a linha tracejada reta.

  • Scale-Location (ou Spread-Location): Usado para verificar a homogeneidade da variância dos resíduos (homocedasticidade). A linha horizontal com pontos igualmente espalhados é uma boa indicação de homocedasticidade. Este não é o caso do nosso exemplo, onde temos um problema de heterocedasticidade.

  • Residuals vs Leverage: Usado para identificar casos influentes, ou seja, valores extremos que podem influenciar os resultados da regressão quando incluídos ou excluídos da análise. Este gráfico será descrito mais adiante nas próximas seções.

Observação: Os quatro gráficos mostram os 3 pontos de dados mais extremos rotulados com os números das linhas dos dados no conjunto de dados. Eles podem ser potencialmente problemáticos. Você pode querer examiná-los individualmente para verificar se há algo especial para o assunto ou se podem ser simplesmente erros de entrada de dados.

6.5.5. Coeficiente de Determinação \(R^2\)

Em geral, a informação real em um dado é a variação total que contém. Essa ideia, no contexto de Regressão Linear, é traduzida pelo valor de \(R^2\) que é chamado de coeficiente de determinação. O \(R^2\) nos diz a proporção da variação na variável dependente (resposta) que foi explicada pelo modelo linear. Matematicamente, essa medida é dada por:

\[R^2 = 1 − \dfrac{SSE}{SST}\] onde, SSE é a soma de quadrados dos erros descrita por:

\[SSE = \sum (y_i−\hat{y}_i)^2\]

e, SST é a soma de quadrados total descrita por:

\[SST = \sum (y_i−\bar{y})^2\]

Aqui, \(\hat{y}_i\) é o valor ajustado para a observação i e \(\bar{y}\) é a média de Y. Em geral, espera-se que esse valor seja próximo de 1 para que o modelo seja “eficaz”. Todavia, se isso não ocorre, não descartamos necessariamente um modelo baseado em um valor \(R^2\) baixo. Quando \(R^2\) é baixo, é necessário observar outras medidas como o AIC e a precisão da previsão do modelo ao decidir sobre a eficácia de um modelo.

6.5.6. Critérios AIC e BIC

No contexto de regressão linear, o critério de informação de Akaike - AIC (Akaike, 1974) e o critério de informação Bayesiano - BIC (Schwarz, 1978) são medidas da qualidade do ajuste de um modelo estatístico estimado, sendo utilizados principalmente para seleção de modelo. Ambos os critérios dependem da maximização da função de verossimilhança L do modelo estimado, e são, matematicamente, definidos por:

\[AIC = −2 \ln(L) + 2k\]

e,

\[BIC = -2\ln(L) + k \ln(n)\] em que \(k\) é o número de parâmetros do modelo, e \(n\) é o tamanho da amostra. No R, para calcular essas duas medidas, fazemos o uso das funções AIC e BIC, isto é,

## Uso da função AIC():

# AIC(object, ..., k = 2)

AIC(linearMod)
## [1] 419.1569
## Uso da função BIC():

# BIC(object, ...)

BIC(linearMod)
## [1] 424.8929

6.5.7. Critérios de Seleção

Selecionar modelos não é uma tarefa muito simples ou fácil, no entanto, existem algumas métricas que nos auxiliam nesse processo. As métricas mais comuns a serem observadas durante a seleção do modelo são:

Estatística Critério
\(R^2\) Quanto maior, melhor (> 0,70)
Estatística F Quanto maior, melhor
Erros-Padrão Quanto mais próximo de zero, melhor
Estatística t Deve ser maior que 1,96 ou menor que -1,96
AIC Quanto mais baixo melhor
BIC Quanto mais baixo melhor
MAPE (Erro Percentual Médio Absoluto) Quanto menor, melhor
MSE (Erro Quadrático Médio) Quanto menor, melhor
Min_Max Accuracy = mean(min(actual, predicted)/max(actual, predicted)) Quanto maior, melhor

6.6. Predição de Modelo Linear

Até agora, vimos como construir um modelo de regressão linear usando todo o conjunto de dados. Se o construirmos dessa forma, não há como saber como o modelo se comportará com novos dados. Portanto, a prática preferida é dividir seu conjunto de dados em uma amostra 80:20 (treinamento:teste) e, em seguida, construir o modelo na amostra de 80% e usar o modelo assim construído para prever a variável dependente nos dados de teste.

Fazendo desta forma, teremos os valores preditos do modelo para os dados de 20% (teste) bem como os reais (do dataset original). Calculando as medidas de precisão (como min_max accuracy) e taxas de erro (MAPE ou MSE), podemos descobrir a precisão da previsão do modelo. Agora, vamos ver como realmente fazer isso.

  • Etapa 1: criar amostras de dados de treinamento (desenvolvimento) e teste (validação) a partir dos dados originais.
## Criar base de dados para o treino

set.seed(100)  # Definir uma semente para os resultados serem reprodutíveis
trainingRowIndex <- sample(1:nrow(cars), 0.8*nrow(cars))  # Índices das linhas para a base de dados de treino
trainingData <- cars[trainingRowIndex, ]  # Base de dados de treino
head(trainingData)
##    speed dist
## 10    11   17
## 38    19   68
## 48    24   93
## 25    15   26
## 14    12   24
## 44    22   66
## Criar base de dados para o teste

testData  <- cars[-trainingRowIndex, ]   # Base de dados de teste
head(testData)
##    speed dist
## 3      7    4
## 5      8   16
## 17    13   34
## 24    15   20
## 28    16   40
## 32    18   42
  • Etapa 2: Desenvolver o modelo nos dados de treinamento e usá-lo para prever a distância nos dados de teste.
## Criar o modelo linear para a base de treino

lmMod <- lm(dist ~ speed, data=trainingData)

## Fazer a predição

distPred <- predict(lmMod, testData)
  • Etapa 3: Revise as medidas de diagnóstico.
## Sumário do modelo

summary(lmMod)
## 
## Call:
## lm(formula = dist ~ speed, data = trainingData)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -24.726 -11.242  -2.564  10.436  40.565 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -20.1796     7.8254  -2.579   0.0139 *  
## speed         4.2582     0.4947   8.608 1.85e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 15.49 on 38 degrees of freedom
## Multiple R-squared:  0.661,  Adjusted R-squared:  0.6521 
## F-statistic: 74.11 on 1 and 38 DF,  p-value: 1.848e-10
## AIC e BIC
AIC (lmMod) 
## [1] 336.6933
BIC (lmMod)
## [1] 341.7599
  • Etapa 4: Calcule a precisão da previsão e as taxas de erro.

Uma simples correlação entre os valores reais e previstos pode ser usada como uma forma de medida de precisão. Uma maior correlação na previsão implica que os valores reais e previstos tenham movimento direcional semelhante, ou seja, quando os valores reais aumentam, os valores previstos também aumentam e vice-versa.

## Previsões atuais

actuals_preds <- data.frame(cbind(actuals=testData$dist, predicteds=distPred))

## Correlação

correlation_accuracy <- cor(actuals_preds)

## Visualização das previsões

head(actuals_preds)
##    actuals predicteds
## 3        4   9.627845
## 5       16  13.886057
## 17      34  35.177120
## 24      20  43.693545
## 28      40  47.951757
## 32      42  56.468182

Agora, calculemos a medida Min_Max Accuracy e MAPE:

## Min_Max Accuracy:

min_max_accuracy <- mean(apply(actuals_preds, 1, min) / apply(actuals_preds, 1, max))  
min_max_accuracy 
## [1] 0.7311131
# MAPE = mean(abs(predicteds−actuals)actuals):

mape <- mean(abs((actuals_preds$predicteds - actuals_preds$actuals))/actuals_preds$actuals)  
mape
## [1] 0.4959096

7. Séries Temporais

7.1. Introdução

Uma série temporal (ST) é um conjunto de observações, geralmente equiespaçadas, obtidas a partir da medição/observação de uma variável ao longo do tempo. A frequência de observação/medição de uma ST pode variar dependendo do fenômeno observado: minuto, diária, semanal, mensal, anual etc. A partir da análise de ST, é possível obter subsídios para a escolha de um modelo adequado para modelar a série, escolhido dentro de uma classe de modelos pré-existentes. Uma vez construído, um modelo de ST pode ser utilizado para efetuar previsões probabilísticas sobre o futuro da série e/ou analisar a sensibilidade de algumas variáveis à variável objetivo. A capacidade de realizar previsões é fundamental no processo de tomada de decisões em diversos contextos e em diversos lugares como orgãos públicos e empresas.

Para trabalhar com séries temporais dentro do R, é necessário fazer o uso da função ts(). O ts recebe a série, a data de ínicio e a frequência. Por exemplo, podemos gerar uma série de variáveis aleatórias da normal (um ruído branco) e transformar em série temporal mensal começando em janeiro de 2000:

## Criar os dados
serie <- rnorm(100)

## Criar a série temporal
serie <- ts(serie, start = c(2000,01), freq = 12)

serie
##              Jan         Feb         Mar         Apr         May         Jun
## 2000  1.75737562 -0.13792961 -0.11119350 -0.69001432 -0.22179423  0.18290768
## 2001  0.62286739 -0.52228335  1.32223096 -0.36344033  1.31906574  0.04377907
## 2002  0.98046414 -1.39882562  1.82487242  1.38129873 -0.83885188 -0.26199577
## 2003  0.20169159 -0.06991695 -0.09248988  0.44890327 -1.06435567 -1.16241932
## 2004 -2.07440475  0.89682227 -0.04999577 -1.34534931 -1.93121153  0.70958158
## 2005  1.42830143 -0.89295740 -1.15757124 -0.53029645  2.44568276 -0.83249580
## 2006  0.84287563 -1.45799372 -0.40030592 -0.77641729 -0.36929651  1.24010146
## 2007  0.12838606  1.01811999 -0.25557369 -0.30254101  1.61519068 -0.77371335
## 2008  1.00745738 -0.46956995  0.29789704 -0.41779443                        
##              Jul         Aug         Sep         Oct         Nov         Dec
## 2000  0.41732329  1.06540233  0.97020202 -0.10162924  1.40320349 -1.77677563
## 2001 -1.87865588 -0.44706218 -1.73859795  0.17886485  1.89746570 -2.27192549
## 2002 -0.06884403 -0.37888356  2.58195893  0.12983414 -0.71302498  0.63799424
## 2003  1.64852175 -2.06209602  0.01274972 -1.08752835  0.27053949  1.00845187
## 2004 -0.15790503  0.21636787  0.81736208  1.72717575 -0.10377029 -0.55712229
## 2005  0.41351985 -1.17868314 -1.17403476 -0.33292335  1.36311371 -0.46914734
## 2006 -0.10743381  0.17259351  0.25460127 -0.61453383 -1.42921510 -0.33097543
## 2007  0.42400240 -0.58394698  0.41503568 -1.54526166 -0.51874950 -0.27979155
## 2008
## Fazer o gráfico da série temporal
autoplot(serie)

Séries temporais possuem três tipos de padrões, também chamados de componentes:

  • Tendência: ocorre quando a variável da série temporal apresenta um aumento ou diminuição a longo prazo;
  • Sazonalidade: corresponde a um padrão fixo que se repete no mesmo período de tempo (Ex.: aumento das vendas de roupa de praia no verão);
  • Ciclos: ocorre quando os dados mostram subidas e quedas que não possuem um período fixo;
  • Erro aleatório: diz respeito aos movimentos irregulares explicados por causas desconhecidas.

No geral, as séries Temporais podem exibir uma grande variedade de padrões que podem ser modelados separadamentes, o que pode ajudar o analista a entender melhor os dados e até mesmo a melhorar as previsões. Por exemplo, se assumirmos que a série segue um modelo aditivo, então, matematicamente, ela pode ser descrita pela equação \(y_t = S_t + T_t + E_t\), onde \(E_t\) é o componente do erro no período \(t\). Por outro lado, se a série for melhor descrita por um modelo multiplicativo, então a equação se torna \(y_t = S_t \cdot T_t \cdot E_t\). Assim, para se decidir se uma série segue um modelo aditivo ou multiplicativo, observa-se a magnitude dos períodos sazonais ou a variância da tendência cresce conforme o nível (valores absolutos) da série cresce.

7.2. Modelos de Média Móvel

Embora seja meio datada e tenha dado espaço para técnicas mais avançadas de decomposição, a média móvel é a base de muitos métodos de análises de séries temporais e uma importante etapa para estimar o componente de tendência de uma série. Na prática, os modelos de média móvel utilizam valores passados de erro de previsão de maneira semelhante a um modelo de regressão:

\[y_t = c + e_t + \theta_1 e_{t − 1} + \theta_2 e_{t − 2} + \ldots + \theta_q e_{t − q}\]

O modelo acima é chamado de modelo MA(q) e pode ser interpretado como um modelo onde \(y_t\) é uma média ponderada dos erros de previsão passados. No R, para trabalhar com os modelos de médias móveis, podemos fazer o uso do pacote BETS por meio da função ma(). Para exemplificar, vamos considerar a base de dados energia disponível no pacote BETS.

## Carregar pacotes

library(BETS)
## Warning: package 'BETS' was built under R version 4.2.2
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## Registered S3 methods overwritten by 'forecast':
##   method                 from     
##   autoplot.Arima         ggfortify
##   autoplot.acf           ggfortify
##   autoplot.ar            ggfortify
##   autoplot.bats          ggfortify
##   autoplot.decomposed.ts ggfortify
##   autoplot.ets           ggfortify
##   autoplot.forecast      ggfortify
##   autoplot.stl           ggfortify
##   autoplot.ts            ggfortify
##   fitted.ar              ggfortify
##   fortify.ts             ggfortify
##   residuals.ar           ggfortify
## 
## Attaching package: 'BETS'
## The following object is masked from 'package:stats':
## 
##     predict
library(forecast)
## Warning: package 'forecast' was built under R version 4.2.2
## 
## Attaching package: 'forecast'
## The following object is masked from 'package:LaplacesDemon':
## 
##     is.constant
library(tidyverse)
library(ggplot2)

## Buscar e baixar a série temporal (no caso, a industria com código 1404)

# energia <- BETSget(1404)

# Salvar os dados

# saveRDS(energia, "datasets/ts_energia.Rda")

## Carregar os dados

energ   <- readRDS("datasets/ts_energia.Rda")
energia <- ts(energ$value)

## Visualizando a série temporal

autoplot(energia) +
  labs(x = NULL, y = "Consumo (Gwh)")

Com a série temporal em mãos, vamos aplicar, então, nosso modelo de médias móveis usando a função ma() do pacote BETS:

## Carregar pacotes

library(BETS)
library(forecast)
library(tidyverse)

## Carregar os dados

energ   <- readRDS("datasets/ts_energia.Rda")
energia <- ts(energ$value, start = 1, end = 42, frequency = 12)

## Gráfico da série tempora contra uma media movel de ordem de 3 meses:

# Uso da função ma():

# ma(x, order, centre = TRUE)

plot(energia)
ma(energia, 3) %>% lines(col = "red", lwd = 1)

# Como a média movel de 3 meses nao foi suficiente, vamos aumentar o periodo para 12 e 24 meses:

ma(energia, 12) %>% lines(col = "blue", lwd = 2)
ma(energia, 24) %>% lines(col = "green", lwd = 3)

Do gráfico, podemos concluir que a curva que apresenta menos flutuações sazonais é verde, referente à média móvel de 24 períodos. Mesmo assim, pode-se dizer que essa decomposição não foi satisfatória, devido a curva apresentar perturbações mesmo usando um período longo (24 meses) para sua estimação.

7.3. Modelo ARIMA

No estudo de séries temporais, a combinação entre os métodos de diferenciação e os modelos de autoregressão e média móvel resultam em um modelo ARIMA (AutoRegressive Integrated Moving Average model) não-sazonal, que pode ser descrito matematicamente como:

\[Y_t = c + \phi_1 Y_{t-1} + \ldots + \phi_p Y_{t-p} + \theta_1 + \theta_1 e_{t-1} + \ldots + \theta_q e_{t-q} + e_t\] Ond \(Y_t\) é a série diferenciada. A equação acima é o que descreve o modelo ARIMA(p, d, q), onde:

  • p: é a ordem do modelo autoregressivo.
  • d: é o grau de diferenciação.
  • q: é a ordem do modelo de média móvel.

Em termos práticos, é naturalmente complicado selecionar valores apropriada para cada um desses três parâmetros, no entanto, ao trabalhar com o R, há uma vantagem. No R, podemos partir do pacote forecast por meio da função auto.arima() que faz a seleção desses valores automaticamente automaticamente. Nesse sentido, para exemplificar o uso do modelo ARIMA, iremos trabalhar com a série temporal AirPassengers que descreve o número de passageiros contabilizados mensalmente ao longo do período de 1949 - 1960, e está disponível no R. Em séries temporais, em geral, para que os valores dos termos “p”, “d” e “q” sejam encontrados, e o ajuste seja viabilizado, deve-se recorrer às funções de autocorrelação e de autocorrelação parcial da teoria de séries temporais. No R, podemos trablhar com essas funções diretamente por meio da função ggtsdisplay do pacote forecast, isto é,

## Carregar pacote

library(forecast)

## Carregar os dados

data(AirPassengers)

## Fazer os gráficos de autocorrelação

# Uso da função ggtsdisplay():

# ggtsdisplay(x, plot.type = c("partial", "histogram", "scatter", "spectrum"),
#             points = TRUE, smooth = FALSE, lag.max, na.action = na.contiguous,
#             theme = NULL, ...)

ggtsdisplay(AirPassengers)

A partir dos gráficos:

  • ACF - função de autocorrelação
  • PACF - função de autocorrelação parcial

Para a determinação da ordem de autoregressão (p), do grau de diferenciação (d) e da componente de médias móveis (q), deve-se realizar a observação do comportamento dos “lags” manifestados por cada gráfico de autocorrelação (ACF e ÁCF). “Nos gráficos gerados, as linhas tracejadas azuis são os limites de significância ou intervalos de confiança; sempre que houver uma ultrapassagem, diz-se que há, ali, um lag com significância”.

Para que o número de diferenciações seja encontrado com maior facilidade, as funções ndiffs() e nsdiffs() podem ser empregadas. Ambas retornam a quantidade de diferenciações necessárias para a estabilização da estacionariedade e da variânica da série. No entanto, a determinação dos valores dos termos pode não ser exata, uma vez que o autor da análise pode incorrer em diversos erros e gerar diversos tipos de modelos. Devido a isso, é necessária a utilizaçaõ de uma métrica com o o intuito de se aferir a qualidade do modelo gerado. Usualmente, se utiliza os valors de AIC e BIC, por exemplo, gerados após o ajuste do modelo. Entendendo essa dificuldade, dentre as ferramentas disponibilizadas pelo pacote forecast, está a função auto.arima() que realiza a verificação dos possíveis modelos gerados a partir da série temporal em questão, visando ao ajuste ideal. Voltando, então, a nossa série temporal AirPassengers, tem-se que:

## Modelos de treino e teste

modelo_treino <- ts(AirPassengers[1:120]) # Amostra treino
modelo_teste  <- ts(AirPassengers[121:144]) # Amostra teste

# Modelo ajustado

modelo        <- auto.arima(modelo_treino, stepwise = FALSE) # Criação do modelo
modelo
## Series: modelo_treino 
## ARIMA(2,1,2) with drift 
## 
## Coefficients:
##          ar1      ar2      ma1     ma2   drift
##       1.6431  -0.9066  -1.8743  0.9688  2.1761
## s.e.  0.0371   0.0361   0.0572  0.0595  0.7107
## 
## sigma^2 = 488.2:  log likelihood = -537.51
## AIC=1087.03   AICc=1087.78   BIC=1103.7

Como pode-se perceber, o modelo ajustado foi um ARIMA, devido à ausência de componentes sazonais. Agora, suponha que nosso objetivo seja realizar a previsão para os próximos 24 períodos, o que permitirá a visualização da acurácia do modelo para a realização de previsões. Neste caso,

## Previsão
previsao <- forecast(modelo, h = 24)

## Leitura da previsão
head(previsao$lower) # Limite inferior
## Time Series:
## Start = 121 
## End = 126 
## Frequency = 1 
##          80%      95%
## 121 314.4380 299.4384
## 122 324.9450 306.0139
## 123 347.5203 327.4080
## 124 372.3897 352.1691
## 125 391.5983 371.3388
## 126 400.8089 380.3839
head(previsao$upper) # Limite superior
## Time Series:
## Start = 121 
## End = 126 
## Frequency = 1 
##          80%      95%
## 121 371.1076 386.1071
## 122 396.4682 415.3992
## 123 423.5063 443.6186
## 124 448.7850 469.0056
## 125 468.1406 488.4001
## 126 477.9761 498.4010
## Gráfico da previsão
autoplot(previsao, predict.colour = "red") + 
         labs(x = "Tempo", y = "Passageiros", title = "Previsao usando o forecast") +
         theme_test() + scale_x_continuous(breaks = seq(0,150,15)) +
         scale_y_continuous(breaks = seq(100, 600, 50)) + geom_vline(xintercept = 120, lty = "dashed")
## Scale for x is already present.
## Adding another scale for x, which will replace the existing scale.

Verificação da acurácia da previsão com a amostra teste:

accuracy(modelo_teste, as.numeric(previsao$mean))
##                 ME     RMSE      MAE       MPE     MAPE
## Test set -37.34932 67.58919 46.91646 -8.559127 10.94617

De acordo com a acurácia, podemos perceber que o modelo arima não é infalível, mas dependendo da qualidade do ajuste de dados, pode ser uma ferramenta poderosa para a análise de séries temporais. Cabe ressaltar que há outras ferramentas com as quais se pode realizar previsões, como as redes neurais, por exemplo.

7.4. Modelo de Cointegração

No geral, a desvantagem de utilizarmos modelos ARIMA para modelar séries temporais (especificamente, as econômicas) está na perda de informações importantes, principalmente, as de longo prazo. Isso por que ao diferenciair uma série para estacionarizá-la, muitas características se perdem (a constante, por exemplo). Uma solução para esse problema está no conceito de séries cointegradas ou, simplesmente, cointegração, que integra relaçãos de curto prazo com equilíbrios de longo prazo que podem ser estimados em separado ou conjuntamente.

Um conceito mais formal sobre cointegração foi proposto por Engle e Granger (1987) e nos possibilita trabalhar com regressões que envolvem variáveis de processos integrados de ordem um (I(1)) e as torna significativas. De acordo com a definição de Engle e Granger (1987), tem-se que:

Os elementos do vetor \(X_t = (x_1, x_2, \ldots, x_n)'\) são ditos cointegrados de ordem (d,b), denotados por \(X_t\sim CI(d,b)\), se:

  • Todos elementos \(X_t\) são integrados de ordem d, ou seja, são I(d).

  • Existe um vetor não-nulo, \(\beta\), tal que \(\alpha_t = X_t^{'}\beta \sim I(d-b), b > 0\). O vetor \(\beta\) é chamado de vetor de cointegração.

Para exemplificar esse conceito, no R, iremos aplicar a teoria da cointegração para definir uma estratégia de operação na bolsa de valores. A ideia é simples: se duas séries tem dependência de longo prazo e, no curto prazo uma série temporal se descola da outra, espera-se que, após algum tempo, haverá uma reversão para a outra série temporal. Para alcançar nosso objetivo, as seguintes atividades serão desenvolvidas:

  • Vamos trabalhar com as séries de ações de dois dos principais bancos privados brasileiros: Bradesco e Itaú.

  • Antes de iniciar nossa estratégia de operação, vamos fazer uma análise exploratória dessas duas séries temporais;

  • Criar uma estratégia de curto prazo para operar as ações;

  • Long-Short através de Cointegração: um exemplo usando o pacote PairTrading.

Primeiramente, vamos consultar o valor das ações desses dois bancos e observa-las graficamente.

## Carregar o pacote quantmod para pegar os valores de ações

require(quantmod)
## Loading required package: quantmod
## Warning: package 'quantmod' was built under R version 4.2.2
## Loading required package: TTR
## Base de dados: Banco Bradesco S.A.

getSymbols('BBDC4.SA',src='yahoo')
## [1] "BBDC4.SA"
## Agora, fazemos o gráfico da série temporal

chartSeries(BBDC4.SA)

## Base de dados: Itaú S.A. - Investimentos Itaú S.A.

getSymbols('ITUB4.SA',src='yahoo')
## [1] "ITUB4.SA"
## Agora, fazemos o gráfico da série temporal

chartSeries(ITUB4.SA)

Agora que temos ambas as séries gerais, podemos estar interessados, por exemplo, em um período específico da série. Vamos supor que esse período seja relacionado ao últimos 4 meses. Então,

## Bradesco

chartSeries(BBDC4.SA,subset = 'last 4 months')

## Itaú

chartSeries(ITUB4.SA,subset = 'last 4 months')

Em geral, as estratégias de arbitragem estatística são baseadas em encontrar uma série temporal que possua a característica de estacionariedade ou reversão à média. Isto significa que é possível identificar situações em que a série divergiu de seu comportamento histórico e prever com alguma segurança que a série convergirá ou reverterá para um comportamento “médio”. O conceito de cointegração formaliza matematicamente este comportamento e permite a realização de testes estatísticos para detectar séries com este comportamento.

No contexto de operações com pares de ativos (pairs trading), a existência de uma relação de cointegração entre as séries de preços de dois ativos significa que pode ser possível realizar operações lucrativas de arbitragem. Por outro lado, se o par não for cointegrado, será impossível encontrar uma relação consistente para operar o par.

É necessário termos um teste para identificar quais pares de ações são cointegrados. Mesmo dentro do universos dos pares que são cointegrados, não há garantia de sucesso. É preciso que o par possua algumas características específicas para que uma estratégia de arbitragem seja consistentemente lucrativa:

  • Relação de cointegração estável ao longo do tempo
  • Reversão frequente do spread à média
  • Variabilidade razoavelmente grande nas divergências

Neste contexto, quais são os passos para operar com pares de ações? Neste caso, os seguintes passos devem ser seguidos:

  1. Selecionar duas ações que movem similarmente;

  2. Vender as ações com preço elevado e comprar as ações com preço baixo;

  3. Monitorar as diferenças entre as duas ações (isso no curto prazo);

  4. No longo prazo, usar a teoria da cointegração para fazer esse monitoramento;

  5. Regra de Decisão com base no Spread:

\[\text{Spread} = \log(Y_t) - (\alpha + \beta\log(X_t))\]

onde \(Y_t\) e \(X_t\) são ações. Logo, se Spread for muito alto, compra-se \(X_t\), vende-se \(Y_t\); todavia, se Spread for muito baixo, compra-se \(Y_t\), vende-se \(X_t\).

No R, para, então, estimar o Spread iremos fazer o uso do pacote PairTrading. No entanto, esse pacote não está mais disponível nas versões mais novas do R, dessa forma, para trabalharmos com o pacote iremos utilizar o pacote que está disponível no Github. Utilizando este pacote vamos estimar o Spread entra as ações do Itaú e do Bradesco. A primeira coisa que precisamos fazer é estimar a regressão linear abaixo:

## Instalar e carregar os pacotes

# install.packages("devtools")
library(devtools)
## Warning: package 'devtools' was built under R version 4.2.2
## Loading required package: usethis
# install_github("cran/PairTrading")
library(PairTrading)
## Loading required package: tseries
## 
## Attaching package: 'tseries'
## The following object is masked from 'package:LaplacesDemon':
## 
##     read.matrix
## Organizar os dados

BBDC4.SA_2013 <- BBDC4.SA['2013::'] 
ITUB4.SA_2013 <- ITUB4.SA['2013::'] 
pairs <- cbind(BBDC4.SA_2013$BBDC4.SA.High,ITUB4.SA_2013$ITUB4.SA.High)

## Estimando o Spread

reg <- EstimateParameters(pairs, method = lm)

Agora, para rodar o back-test nós precisamos estimar os parâmetros historicamente de acordo com um determinado período. Este processo pode ser feito por meio da função EstimateParametersHistorically. Esse função faz algo como uma “rolling regression” para estimar os parâmetros. Assim,

## Estimativas dos parâmetros para back-test

params <- EstimateParametersHistorically(pairs, period = 180)

Agora, nós vamos criar o sinal para a operação usando o Spread. A função Simple nos dá uma estratégia de operação bem simples: se o Spread é maior (menor) que um valor específico, nós iremos comprar (vender). Neste nosso caso, vamos fixar, por exemplo, nosso “valor específico” em 0.05, então, baseado na nossa regressão:

\[\log(ITAU_t) = \alpha + \beta \cdot \log(BRAD_t) + u_t\]

onde o Spread é igual ao erro \((u_t = spread)\):

\[spread_t = \log(ITAU_t) − (\alpha + \beta \log(BRAD_t))\]

Então, iremos comprar Bradesco, se o spread for maior que 0.05 e comprar Itaú, se o spread for menor que -0.05. O gráfico abaixo mostra os pontos em que devemos comprar uma ou outra ação em que: “manchas roxas” na margem inferior (spread maior que 0.05), então compra-se Itaú; por outro lado, se “manchas roxas” na margem superior (spread menor que -0.05), compra-se Bradesco.

## Criando os sinais

signal <- Simple(params$spread, 0.05)

## Gráfico dos sinais

plot(params$spread, ylim = c(-0.3, 0.3));par(new=TRUE)
barplot(signal, space = 0, border=yarrr::transparent("blue", trans.val = .95),  
        col = yarrr::transparent("blue", trans.val = .95), xaxt = "n", 
        yaxt = "n", xlab = "", ylab = "")

8. Processos de Otimização

A otimização, em geral, é um processo que usa um modelo matemático rigoroso para determinar a solução mais eficiente para um determinado problema. Para trabalhar com esse processo, primeiramente, devemos definir o objetivo que é uma medida quantitativa do desempenho (por exemplo, lucro, tempo, custo, energia potencial). Em geral, o objetivo pode ser definido como qualquer quantidade (ou combinação delas) representada como um único número.

Os problemas de otimização são necessariamente classificados em três grupos:

  • Programação linear (PL): função objetivo e restrições são lineares.
  • Programação quadrática (PQ): função objetivo é quadrática, mas as restrições são lineares.
  • Programação não-linear (NLP): função objetivo ou pelo menos uma das restrições é não-linear.

No entanto, a estrutura básica de argumentos de um otimizador é sempre a mesma, e é dada por:

# optimizer(objective, constraints, bounds=NULL, types=NULL, maximum=FALSE)

No R, os pacotes mais comuns para otimização são:

Tipo de problema Pacote Rotina
General purpose (1-dim.) Built-in optimize(…)
General purpose (n-dim.) Built-in optim(…)
Linear Programming lpSolve lp(…)
Quadratic Programming quadprog solve.QP(…)
Non-Linear Programming optimize optimize(…)
optimx optimx(…)
General interface ROI ROI ROI_solve(…)

Por exemplo, considere a função abaixo e suponha que nosso objetivo seja encontrar o ponto de mínimo.

f <- function(x)
{
    2 * (x[1] - 1)^2 + 5 * (x[2] - 3)^2 + 10
}

Neste caso, como otimizador, podemos trabalhar com o optim que tem por objetivo realizar processos de minimização, isto é:

r <- optim(c(1, 1), f)

Naturalmente, em qualque processo de otimização, devemos checar se o processo de otimização convergiu para o mínimo. No caso do optim, basta resgatar o argumento convergence e comparar com 0 (pois é um processo de minimização). Se o mesmo nos retorna como resposta TRUE, então nosso processo de otimização convergiu.

r$convergence == 0 
## [1] TRUE

Por fim, verificamos o valor da função objetivo no ponto de mínimo encontrado que é retornado pelo argumento value do optim, isto é:

r$value
## [1] 10

8.1. Otimizador Linear

No caso de otimização linear, vamos considerar um sistema linear, cujo objetivo é minimização, descrito na seguinte forma: \[\min_\mathbf{x}(c^T \mathbf{x}) = \min_\mathbf{x}(c_1 x_1 + ... + c_n x_n)\] sujeito as restrições: \(Ax \geqslant b\), \(x \geqslant 0\). Para facilitar nosso trabalhar, podemos reescrever esse sistema linear, em forma matricial, como:

\[\min_\mathbf{x} \left[\begin{matrix} c_1 \\ c_2 \\ \ldots \\ c_n\end{matrix}\right]^T\left[\begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_n\end{matrix}\right]\] sujeito a: \[\left[\begin{matrix} a_{11} & a_{12} & \ldots & a_{1n} \\ a_{21} & a_{22} & \ldots & a_{2n}\\ \ldots & \ldots & \ldots & \ldots \\ a_{m1} & a_{m2} & \ldots a_{mn}\end{matrix}\right]\left[\begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_n\end{matrix}\right]\geqslant \left[\begin{matrix} b_1 \\ b_2 \\ \ldots \\ b_n\end{matrix}\right]; \left[\begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_n\end{matrix}\right] \geqslant 0\]

Para exemplificar, vamos considerar o seguinte problema de programação linear (PL):

  1. Função objetivo:
    • O objetivo é maximizar o lucro total.
    • Os produtos A e B são vendidos \(\$25\) e \(\$20\), respectivamente.
  2. Restrições de recursos:
    • O produto A requer 20 unidades de recursos, o produto B precisa de 12.
    • Apenas 1800 unidades de recursos estão disponíveis por dia.
  3. Restrições de tempo
    • Ambos os produtos requerem um tempo de produção de 1/15 hora.
    • Um dia de trabalho tem um total de 8 horas.

Pergunta: Qual é a solução ótima desse problema?

Solução: Neste caso, seja \(x_1\) o número de itens produzidos do produto A e \(x_2\) do produto B. A função objetivo, neste caso, maximiza o total de vendas, e é descrita como:

\[\text{Vendas}_{\max} = \max_{x_1,x_2} 25 x_1 + 20 x_2 = \max_{x_1,x_2} \left[\begin{matrix}25 \\ 20\end{matrix}\right]^T\left[\begin{matrix}x_1 \\ x_2\end{matrix}\right]\] sujeita as restrições: \(20 x_1 + 12 x_2 \leqslant 1800\), e \(\dfrac{1}{15} x_1 + \dfrac{1}{15} x_2 \leqslant 8\).

Para resolver esse problema, então, podemos trabalhar com o otimizador do pacote “lpSolve”. Isto é,

library(lpSolve)

# Objetivo
objective.in  <- c(25, 20)

# Restrições
const.mat     <- matrix(c(20, 12, 1/15, 1/15), nrow = 2, byrow = TRUE)
const.rhs     <- c(1800, 8)
const.dir     <- c("<=", "<=")

# Otimizador
optimum       <- lp(direction="max", objective.in, const.mat,const.dir, const.rhs)

Agora, com base no otimizador, temos que os valores ótimos de \(x_1\) e \(x_2\) desse problema de PL e o valor da função objetivo nos pontos ótimos são dados por:

## Valores ótimos para x_1 e x_2
optimum$solution
## [1] 45 75
## Valor da função objetivo no ponto ótimo
optimum$objval
## [1] 2625

8.2. Método Simplex

O método simplex é uma abordagem para resolver manualmente modelos de programação linear usando variáveis de folga, tableaus e variáveis pivô como meio de encontrar a solução ótima de um problema de otimização. Em poucas palavras, podemos resumir um problema de programação linear (PL) como um método para alcançar o melhor resultado dado uma equação máxima ou mínima com restrições lineares. A maioria dos problemas de PL pode ser resolvida usando um software como, por exemplo, o MatLab, mas o método simplex é uma técnica para resolver tais problemas manualmente. Sendo assim, para aplicar o método simplex, precisamos necessariamente dos seguintes passos:

  • Estabelecer a forma padrão do problema de PL;
  • Apresentando variáveis de folga;
  • Criando o tableau;
  • Apresentar as variáveis pivô;
  • Criar um novo tableau;
  • Verificar se o problema está otimizado;
  • Identificar os valores ótimos;

Então, para exemplificar a apresentação desse método, vamos considerar o seguinte problema de PL:

  • Objetivo: Minimizar: \(-\zeta = -8 x_1 - 10 x_2 - 7 x_3\)
  • Restrições:
    • \(x_1 + 3x_2 + 2x_3\leqslant 10\)
    • \(-x_1 - 5x_2 - x_3 \geqslant - 8\)
    • \(x_1, x_2, x_3 \geqslant 0\)

Vamos, então, trabalhar com os passos descritos anteriomente:

  • 1º Passo: Definindo a forma padrão do problema de PL

A forma padrão é o formato ‘baseline’ para todos os problemas de PL antes de resolver o mesmo afim de encontrar a solução ótima e tem três requisitos:

  1. Deve ser um problema de maximização;
  2. Todas as restrições lineares devem estar em uma desigualdade menor que ou igual a;
  3. Todas as variáveis são não-negativas.

Esses requisitos sempre podem ser satisfeitos transformando qualquer problema de PL usando álgebra básica e substituição. A forma padrão é necessária porque cria um ponto de partida ideal para trabalhar com o método simplex da maneira mais eficiente possível, bem como outros métodos de resolução de problemas de otimização. Sendo assim, no nosso caso, para transformar um problema de PL de minimização em um problema de PL de maximização, basta multiplicar os lados esquerdo e direito da função objetivo por -1, isto é,

\[ (-1)\times (-z = -8 x_1 - 10 x_2 - 7 x_3) \rightarrow = z = 8x_1 + 10x_2 + 7x_3\] Agora, nosso objetivo se torna, necessariamente, um problema de PL de maximização. Por outro lado, a transformação de restrições lineares de uma desigualdade maior ou igual a uma desigualdade menor ou igual pode ser feita de forma semelhante ao que foi feito para a função objetivo. Ao multiplicar por -1 em ambos os lados, a desigualdade pode ser alterada para menor que ou igual a, isto é,

\[(-1) \times (-x_1 - 5x_2 - x_3 \geqslant - 8) \rightarrow x_1 + 5x_2 + x_3 \leqslant 8\]

Uma vez que o modelo está na forma padrão, passamos então para o segundo passo do método simplex.

  • 2º Passo: Introduzindo as variáveis de folga

As variáveis de folga são variáveis adicionais que são introduzidas nas restrições lineares para transformá-las de restrições de desigualdade em restrições de igualdade. Se o modelo estiver na forma padrão, as variáveis de folga sempre terão um coeficiente positivo +1. No nosso caso, temos duas restrições, logo teremos duas variáveis de folga, isto é,

\[x_1 + 2x_2 + 2x_3 + s_1 = 10 \\ x_1 + 5x_2 + x_3 + s_2 = 8 \\ x_1, x_2, x_3, s_1, s_2 \geqslant 0\] onde \(s_1, s_2\) são as variáveis de folga. Depois que as variáveis de folga são introduzidas, passamos para o terceiro passo que é a construção do tableau.

  • 3º Passo: Criando o tableau

Um tableau simplex é usado para realizar operações de linha no modelo de programação linear, bem como para verificar a otimização de uma solução. O tableau consiste no coeficiente correspondente às variáveis de restrição linear e nos coeficientes da função objetivo. Para o nosso problema, no tableau abaixo, a linha superior em negrito do tableau indica o que cada coluna representa. As duas linhas a seguir representam os coeficientes das variáveis de restrição linear do modelo de programação linear, e a última linha representa os coeficientes das variáveis da função objetivo.

\(x_1\) \(x_2\) \(x_3\) \(s_1\) \(s_2\) \(\zeta\) \(\beta\)
1 3 2 1 0 0 10
1 5 1 0 1 0 8
-8 -10 -7 0 0 1 0
  • 4º Passo: Verificar se o problema de PL está otimizado

A solução ótima de um modelo de programação linear de maximização são os valores atribuídos às variáveis na função objetivo para fornecer o maior valor \(\zeta\). Todavia, podemos fazer essa verificação pelo tableau. Então, para verificar a otimização usando o tableau, todos os valores na última linha devem conter valores maiores ou iguais a zero. Se um valor for menor que zero, significa que a variável não atingiu seu valor ótimo. Conforme visto na tableau do nosso exemplo, existem três valores negativos na linha inferior, indicando que essa solução não é a ideal. Se um tableau não for ideal, a próxima etapa é identificar a variável pivô e criar, então, um novo tableau.

  • 5º Passo: Identificando as variáveis pivô

A variável pivô é usada em operações de linha para identificar qual variável se tornará o valor unitário e é um fator chave na conversão do valor unitário. A variável pivô pode ser identificada observando a linha inferior do tableau e o indicador. Assumindo que a solução não é ótima, escolha o menor valor negativo na linha inferior; um dos valores que se encontram na coluna desse valor será a variável pivô. Para encontrar o indicador, divida os valores \(\beta\) das restrições lineares por seus valores correspondentes da coluna que contém a possível variável pivô. A interseção da linha com o menor indicador não negativo e o menor valor negativo na linha inferior se tornará a variável pivô.

Em outras palavras, no nosso exemplo, -10 é o menor negativo na última linha. Isso designará a coluna \(x_2\) para conter a variável pivô. Assim, aplicando as condições citadas acima (no caso, fazemos \(b/x_i\)) para o indicador, obtemos um valor de 10/3 para a primeira restrição e um valor de 8/5 para a segunda restrição. Devido a 8/5 ser o menor indicador não negativo, o valor do pivô estará na segunda linha e terá um valor de 5. Com a variável pivô identificada, passamos ao nosso próximo passo que é criar um novo tableau.

  • 6º Passo: Criar um novo tableau

O novo tableau será usado para identificar uma nova possível solução ótima. Agora que a variável pivô foi identificada na 5º Passo, as operações de linha podem ser executadas para otimizar a variável pivô, mantendo o restante do tableau equivalente. Isto é,

  • Para encontrar o valor de \(s_2\) na linha 1, fazemos:
    • Novo valor do tableau = (valor negativo na antiga coluna pivô do tableau) * (valor na nova linha pivô do tableau) + (valor antigo do tableau) = \((-3) \times \frac{1}{5} + 0 = -\frac{3}{5}\);
  • Para encontrar a variável \(x_1\) na linha 3, fazemos:
    • Novo valor do tableau = (valor negativo na antiga coluna pivô do tableau) * (valor na nova linha pivô do tableau) + (valor antigo do tableau) = $10 + (-8) = -6

e assim por diante.Portanto, após realizar as operações, obtemos o novo tableau:

\(x_1\) \(x_2\) \(x_3\) \(s_1\) \(s_2\) \(\zeta\) \(\beta\)
2/5 0 7/5 1 -3/5 0 26/5
1/5 1 1/5 0 1/5 0 8/5
-6 0 -5 0 2 1 16

Observe que ainda temos valores negativos na última linha, então devemos continuar nossa busca da solução ótima. Para isso, repetimos o processo para o novo pivô encontrado. Após todo esse processo, encontramos com tableu final o seguinte tableau:

\(x_1\) \(x_2\) \(x_3\) \(s_1\) \(s_2\) \(\zeta\) \(\beta\)
0 -2 1 1 -1 0 2
1 5 1 0 1 0 8
0 30 1 0 8 1 64

Agora não temos mais valores negativos na última linha, então passamos para o último passo que é identificar a solução ótima.

  • 7º Passo: Identificar os valores ótimos

Uma vez que o tableau seja comprovado como ótimo, os valores ótimos podem ser identificados. Estes podem ser encontrados distinguindo as variáveis básicas e não-básicas. Uma variável básica é uma variável que tem um único valor 1 em sua coluna e o restante ser todo zero. Se uma variável não atender a esse critério, ela é considerada não-básica. Se uma variável não é básica, significa que a solução ótima dessa variável é zero. Se uma variável for básica, a linha que contém o valor 1 corresponderá ao valor \(\beta\). O valor \(\beta\) representará a solução ótima para a variável dada. No nosso exemplo, temos:

  • Variáveis básicas: \(x_1, s_1, z\)
  • Variáveis não-básicas: \(x_2, x_3, s_2\)

Assim, para a variável \(x_1\), o 1 é encontrado na segunda linha. Isso mostra que o valor ideal de \(x_1\) é encontrado na segunda linha dos valores \(\beta\), que é 8. Por outro lado, a variável \(s_1\) tem um valor 1 na primeira linha, mostrando que o valor ideal é 2 na coluna \(\beta\). Devido a \(s_1\) ser uma variável de folga, ela não está realmente incluída na solução ótima, pois a variável não está contida na função objetivo. Já a variável \(\zeta\) tem um 1 na última linha; isso mostra que o valor objetivo máximo será 64 da coluna \(\beta\). Logo, a solução final do nosso problema de PL para cada uma das variáveis é:

\[x_1 = 8; x_2 = 0; x_3 = 0; s_1 = 2; s_2 =0; \zeta = 64\] Portanto, o valor ótimo para o máximo é 64 que é encontrado no ponto (8, 0, 0) da função objetivo.

Em resumo, o método simplex é uma abordagem para determinar manualmente o valor ótimo de um programa linear, produzindo uma solução ótima para satisfazer as restrições dadas e encontrar um valor \(\zeta\) máximo ótimo. No entanto, este método pode ser bastante extensivo se trabalharmos de forma análitica. Sendo assim, uma saída é implenta-lo computacionalmente.

Exercício: Considere a implementação a seguir do método simplex em R. Substitua os valores @@@ pelos argumentos corretos da função, e verifique se o método está implementado corretamente com base nos resultados do exemplo abordado nesta seção.

# simplex3 <- function(obj, const, rhs)
# {
  # obj: vetor de coeficientes da função objetivo
  # const: matriz de coeficientes de equação de restrição, na forma padrão (problema de maximização)
  # rhs: vetor de valores correspondentes do lado direito para restrições. Observe que todas as restrições são "menores ou iguais a"
    
#  co            <- rbind(@@@, @@@)
#  dims          <- dim(@@@)
#  numRow        <- dims[@@@]
#  M             <- @@@
#  tableau       <- @@@
#  print(@@@)
#  
#  dims          <- dim(@@@)
#  numCol        <- dims[@@@]
#  negIndicator  <- @@@
#  
#  if(min(tableau[numRow,])>=0)
#  {
#    print("No negative indicators: algorithm terminates")
#    negIndicator=FALSE
#  }
#  
#  while(@@@)
#  {
#    pColIndex<-which.min(tableau[numRow,])
#    pCol<-tableau[@@@,@@@]
#    if(@@@)
#    {
#       print("The problem is unbounded, no maximum is possible.")
#       return(tableau)  
#    }
#    possibleRows = which(@@@ > @@@)
#    lst<-tableau[@@@,@@@]
#    div<-lst/pCol[possibleRows]
#    minPos<-which.min(@@@)
#    pRowIndex<-possibleRows[minPos]
#    
#    pivot<-tableau[@@@, @@@]
#    tableau[pRowIndex,]<- @@@ * tableau[pRowIndex,]
#    
#    for(rows in c(1:@@@))
#    {
#        if(rows != pRowIndex)
#        {
#          tableau[rows,]<-tableau[rows,] + -1*tableau[@@@,@@@]*tableau[pRowIndex,]    
#        }
#    }
#    print(@@@)
#    if(min(tableau[numRow,])>=@@@)
#    {
#      negIndicator=@@@
#    }
#  }
#  return(tableau)
#}

8.3. Métodos Numéricos

Considere a seguinte função:

\[f(x) = \dfrac{\log(x)}{(1 + x)}\]

Observe que, se desejamos maximizar essa função em relação a x, não é possível fazer isso de forma analítica. Como procedemos então? Neste caso, podemos trabalhar com os otmizadores disponíveis no R ou, então, trabalhar com os métodos clássicos da matemática como o método da bissecção ou Newton-Rapshon. Mas, antes de ir mais a fundo a esses métodos, vamos verificar o comportamento gráfico de f(x).

x           <- seq(1, 5, by = 0.0001)
f           <- function(x)
{
    log(x)/(1 + x)
}
plot(x, f(x), type = 'l', lwd = 2, xlab = 'x', ylab = 'f(x)', ylim = c(0, 0.3))

Baseado neste gráfico, pode-se notar que o máximo de f(x) é próximo de 3. Pensando, então no processo iterativo, podemos escolher \(x^{(0)} = 3.0\) como valor inicial (ou chute inicial). A partir desse valor, atualizamos nossa equação com o objetivo de obter uma estimativa que seja mais próxima do ponto de máximo. Naturalmente, se a nova estimativa não for o máximo, utilizaremos ela para encontrar uma nova estimativa, e repetimos esse processo até encontrar então o ponto de máximo. As atualizações das estimativas, em geral, são baseadas na raíz da derivada da função f que, neste caso, é dada por:

\[f'(x) = \dfrac{\left(1 + \dfrac{1}{x} - \log(x)\right)}{(1 + x)^2}\] que pode ser implementada em R como:

df          <- function(x)
{
    (1 + 1/x - log(x))/(1 + x)^2
}

8.3.1. Método da Bissecção

O primeiro método que temos, então, para resolver nosso problema é o método da bissecção. Neste método, se f’ é contínua em \([a_0, b_0]\) e \(f'(a_0)f'(b_0) \leqslant 0\), então o Teorema do Valor Intermediário nos diz que existe \(x_k \in [a_0, b_0]\) tal que \(f'(x_k) = 0\), e, então, k é ponto ótimo que maximiza f. Naturalmente, para trabalhar com esse método, devemos dividir nosso intervalo em subintervalos \([a_0,b_0] \supset [a_1, b_1] \supset \ldots\) até encontrar o ponto \(x_k\). Comecemos, então, com \(x_0 = \dfrac{a_0 + b_0}{2}\) sendo um valor inicial. Então, as equações que atualizam o processo iterativo do método da bissecção são:

\[ [a_{t+1}, b_{t+1}] = \begin{cases}[a_t, x^{(t)}], \text{ se } f'(a_t)f'(x^{(t)}) \leqslant 0 \\ [x^{(t)}, b_t], \text{ se } f'(a_t)f'(x^{(t)}) > 0\end{cases}\] e,

\[x^{(t+1)} = \dfrac{1}{2} (a_{t+1} + b_{t+1})\] OBS: Se f tiver mais de uma raiz no intervalo inicial, é fácil ver que a bissecção encontrará uma delas, mas não encontrará as demais.

Voltemos, então, ao nosso exemplo inicial, o caso da função f. Para aplicar o método da bissecção nessa função, temos os seguintes parâmetros:

  • a = valor inicial do extremo esquerdo
  • b = valor inicial do extremo direito
  • x = valor inicial
  • itr = número de iterações do método
  • f = função objetivo
  • df = primeira derivada da função objetivo

Agora, defina como valores iniciais: \((a_0, b_0) = (1,5)\) e 40 iterações do método, isto é,

a           <- 1
b           <- 5
x           <- a+(b-a)/2
itr         <- 40

Implementando o método, embora de uma maneira não eficiente, temos:

for (i in 1:itr)
{
    if(df(a) * df(x) < 0)
    {
        b <- x
    }
    else
    {
        a <- x
    }
    x <- a+(b-a)/2
}

Assim, como resultados, obtemos:

# Ponto ótimo x_k
x
## [1] 3.591121
# Função objetivo calculada no ponto ótimo
f(x)
## [1] 0.2784645
# Derivada calculada no ponto ótimo
df(x)
## [1] -1.121895e-14

Graficamente, pelo método da bissecção, o ponto de máximo desejado é representado por:

Na prática, não podemos permitir que o procedimento seja executado indefinidamente, por isso exigimos uma regra de parada, baseada em alguns critérios de convergência, para desencadear o fim da aproximação sucessiva. A cada iteração, a regra de parada deve ser verificada. Quando os critérios de convergência são atendidos, o novo \(x^{(t+1)}\) é tomado como solução. Há duas razões para parar: se o procedimento parece ter alcançado uma convergência satisfatória ou se parece improvável que o faça em breve. Sendo assim, duas regras de parada são importantes:

  • Critério de convergência absoluta: \(\left|x^{(t+1)} - x^{(t)}\right| < \epsilon\)
  • Critério de convergência relativo: \(\dfrac{\left|x^{(t+1)} - x^{(t)}\right|}{\left|x^{(t)}\right|} < \epsilon\)

onde \(\epsilon\) é uma constante de precisão. Na prática, a imprecisão numérica em um computador pode impedir a convergência. Para a maioria dos métodos de aproximação iterativa, é mais seguro adicionar uma pequena correção a uma aproximação anterior do que iniciar uma nova aproximação do zero. No caso do método da bissecção, ele se torna mais estável numericamente quando o ponto final atualizado é calculado como, digamos, \(a_{t+1} = a_t + \frac{b_t − a_t}{2}\) em vez de \(a_{t+1} = \frac{a_t + b_t }{2}\).

Em resumo, podemos concluir que o método da bissecção é um exemplo de método de bracketing, ou seja, um método que limita uma raiz dentro de uma sequência de intervalos aninhados de comprimento decrescente. A bissecção é uma abordagem bastante lenta: requer um número bastante grande de iterações para atingir a precisão desejada, em relação a outros métodos como, por exemplo, Newton-Raphson.

8.3.2. Método de Newton-Raphson

Um dos métodos iterativos mais rápidos para se encontrar uma raíz de uma dada função é o método de Newton, especialmente em aplicações univariadas. Suponha que g’ é continuamente diferenciável e que g’‘(x_k) é diferente de 0. Na iteração t, a abordagem aproxima g’(x_k) pela expansão linear em série de Taylor:

\[0 = g'(x_k) \approx g'(x^{(t)} + (x_k - x^{(t)})g''(x^{(t)})\]

Como g’ é aproximado por sua reta tangente em \(x^{(t)}\), parece sensato aproximar a raiz de g’ pela raiz da reta tangente. Assim, resolvendo a equação acima para \(x_k\), obtemos:

\[x_k = x^{(t)} - \dfrac{g'(x^{(t)})}{g''(x^{(t)})} = x^{(t)} + h^{(t)}\]

onde \(h^{(t)} = - \frac{g'(x^{(t)})}{g''(x^{(t)})}\) é chamado de incremento de Newton.

Naturalmente, a atualização do método de Newton é descrita pela expressão \(x^{(t+1)} = x^{(t)} + h^{(t)}\). No entanto, podemos também trabalhar com a atualização do método encontrando a solução da aproximação quadrática da série de Taylor para \(g(x_k)\) descrita por:

\[g(x^{(t)}) + (x_k - x^{(t)})g'(x^{(t)}) + (x_k - x^{(t)})^2 \dfrac{g''(x^{(t)})}{2}\]

Para exemplificar, entãom voltemos ao caso da função f descrita no método da bissecção. Para aplicar o método de Newton nessa função, consideramos os seguintes parâmetros:

  • x = valor inicial
  • itr = número de iterações do método
  • f = função objetivo
  • f.prime = primeira derivada da função objetivo
  • f.2prime = segunda derivada da função objetivo

Agora, defina como valores iniciais: x = 3 e 40 iterações do método, isto é,

x           <- 3
itr         <- 40

Implementando o método, embora de uma maneira não eficiente, temos:

f           <- function(x)
{
    log(x)/(1+x)
}

f.prime     <- function(x)
{
    (1+(1/x)-log(x))/((1+x)^2)
}

f.2prime    <- function(x)
{
    (-1/((x^2)+(x^3)))-2*(1+(1/x)-log(x))/((1+x)^3)
}

for(i in 1:itr)
{
    x = x - f.prime(x)/f.2prime(x)
}

Portanto, como resultados, obtemos:

# Ponto ótimo x_k
x
## [1] 3.591121
# Função objetivo calculada no ponto ótimo
f(x)
## [1] 0.2784645
# Derivada calculada no ponto ótimo
df(x)
## [1] 1.053423e-17

Graficamente, pelo método de Newton, o ponto de máximo desejado é representado por:

8.4. Máxima Verossimilhança

Quando falamos de processos de otimização, pensamos sempre maximizar ou minimizar uma expressão. Em termos de probabilidade, não seria diferente. Nesta seção, introduziremos um dos métodos mais comuns para se obter os parâmetros de um modelo de probabilidade ajustado a uma base de dados que é o metodo da máxima verossimilhança.

O princípio da verossimilhança afirma que devemos escolher aquele valor do parâmetro desconhecido que maximiza a probabilidade de obter a amostra particular observada, ou seja, o valor que torna aquela amostra a mais provável. Matematicamente, dada uma amostra aleatória \((X_1, \ldots, X_n)\) de uma variável aleatória X, a função de verossimilhança é definida por:

\[L(\theta) = \prod_{i=1}^{n} f_\theta (x_i)\]

em que \(f_\theta\) é a função densidade de probabilidade no caso contínuo, ou a função de probabilidade no caso discreto. O estimador de máxima verossimilhança é obtido a partir da maximização de \(L\) ou, equivalentemente, \(\ell(\theta) = \ln(L(\theta))\). Dentro do R, existem várias formas de se maximizar a função de verossimilhança, sendo as mais populares utilizando o otmizador optim(), o otimizador maxLik do pacote maxLik, ou o otimizador fitdist do pacote fitdistrplus. Os dois últimos, em particular, são baseados, por padrão, no método de Newton-Rapshon. Para exemplificar como funcionar tais métodos, consideramos uma amostra aleatória de tamanho 50 gerada de uma distribuição exponencial com parâmetro \(\lambda = 5\).

## Carregar os pacotes

library(maxLik)
## Loading required package: miscTools
## Warning: package 'miscTools' was built under R version 4.2.2
## 
## Please cite the 'maxLik' package as:
## Henningsen, Arne and Toomet, Ott (2011). maxLik: A package for maximum likelihood estimation in R. Computational Statistics 26(3), 443-458. DOI 10.1007/s00180-010-0217-1.
## 
## If you have questions, suggestions, or comments regarding the 'maxLik' package, please use a forum or 'tracker' at maxLik's R-Forge site:
## https://r-forge.r-project.org/projects/maxlik/
library(fitdistrplus)
## Gerar uma amostra aleatória de tamanho 50 da distribuição exponencial com parâmetro lambda = 5

x <- rexp(50, rate = 5)

## Definir a função densidade de probabilidade, e função acumulada (necessárias para o fitdistrplus)

dmyexp <- function(x,lambda)
{
  lambda * exp(-lambda * x)
}

pmyexp <- function(q,lambda)
{
  1 -  exp(-lambda * q)
}

## Definir a função de verossimilhança

ll.exp  <- function(lambda)
{ 
   n    <- length(x)
   ll   <- n*log(lambda) - lambda * sum(x)
   return(ll)
}

## Encontrar os estimadores de máxima verossimilhança

# Uso do optim():

lambda0 <- 4.5
fit1    <- optim(par = lambda0, fn=ll.exp, control = list(fnscale = -1), method = 'Brent', lower = 0, upper = 1000)
fit1$par
## [1] 7.642864
# Uso do maxLik():

lambda0 <- 4.5
fit2    <- maxLik(logLik = ll.exp, start = c(lambda = lambda0))
fit2
## Maximum Likelihood estimation
## Newton-Raphson maximisation, 5 iterations
## Return code 1: gradient close to zero (gradtol)
## Log-Likelihood: 51.68862 (1 free parameter(s))
## Estimate(s): 7.642865
# Uso do fitdistrplus():

lambda0 <- 4.5
fit3    <- fitdist(data = x, distr = 'myexp', start = list(lambda = lambda0))
fit3
## Fitting of the distribution ' myexp ' by maximum likelihood 
## Parameters:
##        estimate Std. Error
## lambda 7.642864   1.080864

Além das alternativas acima, podemos também trabalhar diretamente com o método do Escore de Fisher em vez de fazer o uso de pacotes para encontrar os estimadores de máxima verossimilhança. Tal método pode ser entendido como uma variante estatística do método de Newton-Raphson, sendo resultado da substituição da segunda derivada \(f''(\cdot)\) de um parâmetro \(\theta\) pelo seu valor esperado. Assim, a atualização do método é dada por:

\[\theta^{(i + 1)}=\theta^{(i)} - \dfrac{f'(\theta^{(i)})}{E[f''(\theta^{(i)})]}\] Neste contexto, para exemplificar, vamos implementar uma rotina em R para o método escore de Fisher baseando-se na distribuição de probabilidade contínua Weibull.

# Código 1: Implementação da função log-verossimilhança

logvero     <- function(n, t, theta)
{
    mu        <-    theta[1]
    beta        <-  theta[2]
    l             <-    n * log(beta) - n * beta * log(mu) + (beta - 1) * sum(log(t)) - 1/mu^beta * sum(t^beta)
    return(l)
}

# Código 2: Implementação do vetor escore (vetor gradiente que contém as derivadas da log-verossimilhança).

G                 <- function(n, t,theta)
{
    mu        <-    theta[1]
    beta        <-  theta[2]
    A         <-    t/mu
    U             <-    matrix(nrow = 2)
    U[1]        <-  -n * beta/mu + beta/mu * sum(A^beta) 
    U[2]        <-  n/beta - n * log(mu) + sum(log(t)) - sum(log(A) * A^beta)
    return(U)
}

# Código 3: Implementação da matriz hessiana (matriz Jacobiana do vetor escore).

J                 <- function(n, t,theta)
{
    mu          <-  theta[1]
    beta          <-    theta[2]
    A         <-    t/mu
    H             <-    matrix(nrow = 2, ncol = 2)
    H[1,1]    <-    n * beta/mu^2 - (beta/mu)^2 * sum(A^beta) - beta/mu^2 * sum(A^beta)
    H[1,2]    <-    -n/mu + beta/mu * sum(A^beta * log(A)) + 1/mu * sum(A^beta)
    H[2,1]    <-    H[1,2]
    H[2,2]    <-    -n/beta^2 - sum(A^beta * (log(A))^2)
    return(H)
}

# Código 4: Implementação do método de Newton-Raphson.

set.seed(12345)
n               <-  1000 # Tamanho de amostra
mu_0          <-    1
beta_0        <-    0.5
theta_0       <-    matrix(c(mu_0, beta_0), nrow = 2)
t               <-  rweibull(1000, shape = 2, scale = 1.5) # Gerar os dados
erro          <-    0.0005
itr           <-    1
while(itr < 100)
{
    itr         <- itr + 1 
    theta_1     <- theta_0 - solve(J(n, t,theta_0))%*%G(n, t,theta_0)
    if (max(abs(G(n, t,theta_1) - G(n, t,theta_0))) < erro) break
    theta_0     <- theta_1
    cat(itr,theta_1, "\n")
}
## 2 1.481969 0.9379444 
## 3 1.033923 1.548405 
## 4 1.496041 2.227046 
## 5 1.463714 1.996796 
## 6 1.46633 2.012774 
## 7 1.466341 2.012867
# Conferindo o ajuste:

hist(t, prob=TRUE, ylim = c(0,1))
curve(dweibull(x, scale = theta_1[1], shape=theta_1[2]), add=TRUE) 

9. Geração de Valores Aleatórios

Nas seções anteriores, trabalhamos um pouco com a construção de funções e os principais otimizadores dentro do ambiente R. Nosso foco agora será trabalhar com algo que é suma importância na Estatística Computacional: geração de valores aleatórios para modelos de probabilidade. A geração de números aleatórios nos permite, por exemplo, gerar dados aleatórios para realizar uma simulação de dados com propriedades conhecidas, por exemplo, provenientes de determinadas distribuições de probabilidade para avaliar o desempenho de um teste para detecção de outliers ou o grau com o que o afastamento da normalidade influencia o desempenho do teste t. Além disso, podemos também avaliar níveis níveis cobertura de intervalos de confiança ou grau de vício de estimadores, bem como acessar a qualidade de um método de classificação e a velocidade de processamento de certa tarefa.

Neste contexto, então, iremos trabalhar com dois métodos importantes para gerar dados aleatórios: o método da transformação inversa, e o método de rejeição (ou aceitação rejeição).Essencialmente, para trabalhar com esses métodos devemos levar em conta seis fatores fundamentais:

  1. Velocidade.
  2. Tempo de inicialização.
  3. Comprimento do código.
  4. Independência da máquina, portabilidade.
  5. Número de operações dentro da função.
  6. Simplificidade e legibilidade.

9.1. Transformação Inversa

A ideia do método de transformação inversa é simplesmente representar nossa “complexa” variável aleatória como resultado de uma função aplicada a um variável aleatória uniforme, que nós sabemos como gerar. Em termos matemáticos, ele é baseado no seguinte resultado:

Teorema: Seja F uma função de distribuição contínua em \(\mathbb{R}\) tal que sua inversa seja definida por \(F^{-1}(u) = \inf\{x: F(x) = u, 0 < u < 1\}\). Se a variável aleatória U segue uma distribuição uniforme, \(U(0,1)\), então \(F^{-1}(U)\) tem função de distribuição \(F\). Ainda, se uma variável aleatória X tem função de distribuição \(F\), então \(F(X)\sim U(0,1)\). A função inversa, \(F^{-1}\) obtida é chamada de função quantil.

Em termos de computação, nosso algoritmo para gerar valores aleatórios com base na transformação inversa é descrito como:

  • 1º Passo: Gerar dados de uma distribuição uniforme, U(0,1).
  • 2º Passo: Retorne \(X = F^{-1}(U)\).

No entanto, para alguns modelos de probabilidade, nem sempre é possível encontrar essa função inversa, no caso, a função quantil. O que fazemos então? Uma saída é, a partir dos otimizadores vistos anteriormente, encontrar a solução númerica. Para exemplificar esse processo, vamos trabalhar com a distribuição exponencial com parâmetro \(\lambda\) com a seguinte função densidade de probabilidade:

\[f(x; \lambda) = \lambda e^{-\lambda x}\] Note que, para essa distribuição, a função de distribuição F é descrita por \(F(x;\lambda) = 1 - e^{-\lambda x}\). Nosso foco aqui é, necessariamente encontrar a função inversa de F com base na distribuição uniforme que, neste caso, é dada por:

\[ F^{-1}(U) = -\dfrac{1}{\lambda}\log(1 - U)\]

Vamos supor agora que não fosse possível encontrar a forma analítica dessa função F da distribuição exponencial. Neste caso, então, precisariamos de uma solução numérica. Para isso, vamos inicialmente definir a expressão analítica da função densidade de probabilidade da distribuição exponencial no R:

my.dexp             <-      function(x, lambda)
{
    lambda*exp(-lambda*x)
}

Agora, precisamos da função de distribuição. Visto que estamos focados em solução numérica, vamos, então, implementar a solução númerica da função de distribuição que, neste caso, é descrita pela rotina:

my.pexp.numerical   <-      function(x, lambda)
{
    my.int          <-      function(x, lambda)
    {
        integrate(my.dexp, lambda = lambda, lower = 0, upper = x)$value
    }
    sapply(x, FUN=my.int, lambda)
}

Com a função de distribuição em mãos, vamos ao procedimento de encontrar a função inversa de F, ou função quantil. Como dependemos de uma solução numérica, vamos trabalhar com a função uniroot que é uma ‘procedure’ baseada no método de Newton-Rapshon. Logo, temos que:

my.qexp.numerical   <-      function(q, lambda)
{ 
  f                 <-      function(P, fixed)                                      
  {
    lambda          <-      fixed$lambda
    q               <-      fixed$q
    criterion       <-      q - my.pexp.numerical(P, lambda)                        
    return(criterion)
  }
  P                 <-      numeric(length(q))                                      
  for(i in 1:length(q))
  {
    fixed           <-      list(lambda = lambda, q = q[i])                         
    root.p          <-      uniroot(f, lower = 0, upper = 100, fixed = fixed)       
    P[i]            <-      root.p$root
  }
  return(P)
}

Pergunta: Será que a solução numérica acima tem o mesmo resultado que a solução analítica considerando o mesmo valor de \(\lambda\)? Para responder essa questão, vamos comparar a nossa implementação com a função qexp do R que define a função quantil da distribuição exponencial de forma analítica. Para nosso experimento, então, vamos considerar o valor de \(q = 0.9\) e \(\lambda = 2\):

qexp(0.9, 2)
## [1] 1.151293
my.qexp.numerical(0.9, 2)
## [1] 1.151299

Calculando a diferença entre as duas, obtemos que:

my.qexp.numerical(0.9, 2) - qexp(0.9, 2)
## [1] 6.906585e-06

ou seja, a diferença entre os resultados é 0.000006 que é aproximadamente igual à 0. Portanto, nossa implementação da solução numérica para encontrar a inversa de F é aceitável.

9.1.1. Geração de Dados de uma Distribuição Exponencial

Vamos considerar a distribuição exponencial. Nosso foco aqui é gerar uma amostra aleatória, digamos, de tamanho \(n = 10000\) e parâmetro \(\lambda = 2\) com base nessa distribuição. A pergunta é: como fazemos isso? De acordo com o método da transformação inversa, seguimos os seguintes passos:

  • 1º Passo: Gerar dados de uma distribuição uniforme, U(0,1).
  • 2º Passo: Retorne \(X = F^{-1}(U)\).

Mas antes, vamos escrever a função quantil da distribuição exponencial (aqui, podemos também utilizar a função qexp), isto é,

my.qexp.analytic    <-      function(q, lambda)
{
  (-log(1-q))/lambda
} 

Para gerar nossa amostra, então, precisamos gerar uma distribuição uniforme e aplicar a função quantil. Para isto, vamos criar a função ‘my.rexp.inverse’:

my.rexp.inverse     <-       function(N, lambda)
  {
    U               <-       runif(N, min=0, max=1)
    rnd.values      <-       my.qexp.analytic(U, lambda)
    return(rnd.values)
}

Por fim, nos resta gerar nossa amostra e verificar se ela, de fato, corresponde a uma distribuição exponencial. Mas como fazemos isso? Bom, o primeiro passo é executar nossa função com \(n = 10000\) e \(\lambda = 2\). Como segundo passo, podemos fazer o histograma dos dados gerados e anexar a curva da distribuição exponencial com \(\lambda = 2\), ou seja,

set.seed(1212) # Semente para reprodução de resultados
amostra <- my.rexp.inverse(1000,2)

hist(amostra, 
      freq=FALSE, 
      breaks=20,
      xlab="x", 
      main=NULL, 
      col="grey")
curve(dexp(x, rate=2), add=TRUE)

Com base no histograma, podemos ver que nossa amostra aletória gerada, de fato, segue uma distribuição exponencial com \(\lambda = 2\). É claro que essa é uma maneira simples e superficial de verificar se a amostra provém da distribuição em questão, para uma verificação mais completa, devemos trabalhar com os métodos e estimação, e verificação de vieses. Agora, vamos supor que desejamos realizar a mesma tarefa, porém, com bse na função quantil analítica. Como geramos nossa amostra, neste caso?

Neste caso, basta trocar a função quantil analítica na função ‘my.rexp.inverse’ pela função quantil numérica, isto é,

my.rexp.inverse2     <-       function(N, lambda)
  {
    U               <-       runif(N, min=0, max=1)
    rnd.values      <-       my.qexp.numerical(U, lambda)
    return(rnd.values)
}

Agora, para gerar os dados, procedemos da mesma maneira que no caso da função quantil analítica, isto é,

set.seed(1212) # Semente para reprodução de resultados
amostra <- my.rexp.inverse2(1000,2)

hist(amostra, 
      freq=FALSE, 
      breaks=20,
      xlab="x", 
      main=NULL, 
      col="grey")
curve(dexp(x, rate=2), add=TRUE)

Novamente, com base no histograma, podemos ver que nossa amostra aletória gerada, de fato, segue uma distribuição exponencial com \(\lambda = 2\). Mas uma coisa que podemos nos questionar é: qual dos métodos é mais rápido? analítico ou numérico? Para responder essa questão, vamos trabalhar com a função system.time() que nos traz o tempo de execução de uma função. Para este experimento, vamos considerar uma amostra aleatória de tamanho \(n = 100000\) com parâmetro \(\lambda = 2\).

set.seed(1212) # Semente para reprodução de resultados
system.time(my.rexp.inverse(1000,2))
##    user  system elapsed 
##       0       0       0
set.seed(1212) # Semente para reprodução de resultados
system.time(my.rexp.inverse2(1000,2))
##    user  system elapsed 
##    0.11    0.02    0.85

Observe que o tempo de sistema, no primeiro caso, é menor do que 0.01 segundo, porém, no segundo caso, temos um tempo de sistema de 0.94 segundos. Esse resultado é esperado uma vez que uma solução numérica tem como base aproximações sucessivas e isso demanda muito tempo computacional. Em suma, utilizamos esse método apenas quando é possível encontrar a solução analítica da função quantil (ou inversa de F); caso isso não seja possível, então, podemos utilizar, por exemplo, um método de geração de dados conhecido por método de rejeição (ou aceitação-rejeição) em vez do método da transformação inversa.

9.2. Aceitação-Rejeição

A ideia do método de rejeição é gerar dados quando não se é possível encontrar a inversa \(F^{-1}\) da função de distribuição F ou quando \(F\) não possui uma expressão anlítica. Em termos matemáticos, este método é baseado no seguinte resultado:

Teorema: Seja \(X\) um vetor aleatório com densidade \(f\) em \(\mathbb{R}^d\), e seja \(U\) uma variável aleatória que segue uma distribuição uniforme no intervalo \([0,1]\), \(U\sim U(0,1)\). Então, \((X, kUf(X))\) tem distribuição uniforme em \(A=\{(x,u): x \in \mathbb{R}^d, 0 \leqslant u \leqslant kf(x)\}\), onde \(k > 0\) é uma constante arbitrária. Ainda, se \((X,U)\) é um vetor aleatório em \(\mathbb{R}^{d+1}\) com distribuição uniforme em A, então X tem densidade \(f\) em \(\mathbb{R}^d\).

Com base no resultado acima, podemos escrever nosso algoritmo para o método da rejeição como: Seja \(g\) uma densidade da qual sabemos como amostrar e para a qual podemos calcular facilmente \(g(x)\). Seja \(e(\cdot)\) um envelope, tendo a propriedade \(e(x) = \frac{g(x)}{\alpha} \geqslant f(x)\) para todo \(x\) para o qual \(f(x) > 0\) para uma dada constante \(\alpha \leqslant 1\), isto é:.

  • 1º Passo: Gerar \(y\) como sendo uma ocorrência da variável aleatória representada por \(G\), isto é, \(Y\sim g\).
  • 2º Passo: Gerar \(u\) como sendo uma ocorrência de uma uniforme padrão, isto é, \(U\sim U(0,1)\).
  • 3º Passo: Se \(u \leqslant \dfrac{f(y)}{kg(y)}\) considerar que \(x = y\) é um valor da distribuição de probabilidade alvo cuja densidade é \(f\), caso contrário, descartar \(y\).
  • 4º Passo: Repetir até atingir o número de valores desejado \(n\).

9.2.1. Gerando Dados de uma Distribução Normal Com Base na Distribuição Cauchy

Para exemplificar o método, vamos considerar a distribuição normal (ou gaussiana) padrão dada pela densidade \(f = \frac{1}{\sqrt{2\pi} }e^{-\frac{1}{2}x^2}\). Nosso objetivo é gerar uma amostra aleatória, digamos, de tamanho \(n = 10000\) para essa distribuição. Sendo assim, com base no método da rejeição, precisamos de uma distribuição que tenha núcleo proporcional a distribuição normal padrão e, na literatura, a distribuição com essa propriedade é a distribuição Cauchy padrão (que chamaremos de densidade \(g = \frac{1}{\pi}\left(1+x^2\right)^{-1}\)), então, podemos trabalhar com a razão entre a normal e a cauchy.

Para entendermos melhor essa questão, vamos avaliar graficamente ambas as curvas e também a razão entre elas. Neste gráfico, podemos perceber que as distribuições são proporcionais, porém a Cauchy padrão ainda não ‘cobre’ totalmente a normal padrão; no entanto, nosso foco é encontrar o valor de \(k\) tal que \(k \cdot g(x)\) ‘cubra’ \(f(x)\).

par(mfrow = c(1, 2))

# Gaussiana e Cauchy.
curve(dnorm(x), -4, 4, ylab = 'Density')
curve(dcauchy(x), add = TRUE, lty = 2)
legend("topright", legend = c("Normal", "Cauchy"), lty = c(1, 2), bty = "n", cex = 0.7)

# Razão entre elas.
curve(dnorm(x)/dcauchy(x), -4, 4, ylab = 'Normal/Cauchy')
abline(v = c(-1, 1), lty = 2)

Matematicamente, a razão entre a normal padrão e a cauchy padrão nos gera a expressão \(\sqrt{\frac{2 \pi}{e}}\) quando \(x = 1\). Logo, para encontrar o valor de \(k\), definimos \(k\) como \(k = \sqrt{\frac{2 \pi}{e}}\). No R:

k <- sqrt(2 * pi/exp(1))
k
## [1] 1.520347

Agora a nossa questão é: **será que a distribuição cauchy padrão com densidade \(g\) cobre a distribuição normal padrão com densidade \(f\) se tivermos \(k\cdot g(x)\). Para responder essa questão, vamos refazer nosso gráfico sob essa nova condição:

curve(k * dcauchy(x), -4, 4,lty = 2,ylim = c(0, k * dcauchy(0)),ylab = "Density")
curve(dnorm(x), add = TRUE)
legend("topright",legend = c("f(x)", "k g(x)"), lty = c(1, 2), bty = "n", cex = 0.7)

Para aplicar o método de rejeição para gerar nossos dados, então, devemos seguir os passos:

  • 1º Passo: Gerar \(y\) como sendo uma ocorrência da variável aleatória representada por \(G\), isto é, \(Y\sim g\).
  • 2º Passo: Gerar \(u\) como sendo uma ocorrência de uma uniforme padrão, isto é, \(U\sim U(0,1)\).
  • 3º Passo: Se \(u \leqslant \dfrac{f(y)}{kg(y)}\) considerar que \(x = y\) é um valor da distribuição de probabilidade alvo cuja densidade é \(f\), caso contrário, descartar \(y\).
  • 4º Passo: Repetir até atingir o número de valores desejado \(n\).

Neste caso, como nos baseamos na distribuição Cauchy padrão, precisamos gerar dados para ela como primeiro passo. Como a função inversa (ou quantil) da distribuição de Cauchy padrão é dada por \(F^{-1} = \tan(\pi (1-u))\), podemos aplicar o método da transformação inversa, neste caso. Sendo assim, para fixar as ideias, vamos gerar um valor da distribuição de Cauchy padrão:

y <- rcauchy(n = 1)
y
## [1] -0.9411749

Agora, vamos ao 2º passo. Neste caso, devemos gerar valores de distribuição uniforme padrão, isto é, \(U(0,1)\). Como geramos apenas um valor da Cauchy padrão, aqui também iremos gerar apenas um valor, isto é,

u <- runif(n = 1)
u
## [1] 0.6649981

Já o 3º passo nos diz que se \(u \leqslant \dfrac{f(y)}{kg(y)}\), consideramos \(x = y\) e aceitamos o valor; do contrário, descartamos o valor. Isto é,

f <- function(x) 
{
  dnorm(x, 0, 1)
}
g <- function(x) 
{
  dcauchy(x, 0, 1)
}
r <- f(y)/(k * g(y))
r
## [1] 0.9983067
if (u < r) {
    x <- y
    print("u < r, então valor aceito.")
} else {
    print("u >= r, então valor descartado.")
}
## [1] "u < r, então valor aceito."

Por fim, o último passo nos diz para repetir esse processo até atingir o tamanho de amostra desejado, ou seja,

set.seed(1212)  # Semente para reprodução de resultados
n <- 1          # Contador de valores aceitos.
l <- 1          # Contador de ciclos.
N <- 100        # Total de número à gerar.

x <- numeric(N) # Vetor vazio

while (n < N) 
{
    y <- rcauchy(n = 1)
    u <- runif(n = 1)
    w <- f(y)/(k * g(y))
    if (u < w) {
        x[n] <- y
        n <- n + 1
    }
    l <- l + 1
}

Após esse processo, podemos calcular as taxas de aceitação, teórica e numérica, do algoritmo. Neste caso, tais taxas são dadas, em porcentagem, por

# Taxa de aceitação:
round(n/l * 100, 2) # Observada
## [1] 72.46
round(1/k * 100, 2) # Teórica
## [1] 65.77

Mas como sabemos se nossos valores gerados são, de fato, provenientes de uma distribuição normal padrão? Ora, para verificar essa questão, podemos trabalhar com o gráfico da função de distribuição (F) dos valores gerados e, em seguida, adicionar a curva normal em cima desse gráfico. Se a curva for aproximadamente igual, então nosso algoritmo de geração é aceitável, isto é, a distribuição de Cauchy padrão é uma ótima escolha para cobrir e gerar dados para a distribuição normal padrão.

plot(ecdf(x))
curve(pnorm(x), add = TRUE, col = 2)

Portanto, com base no gráfico da função de distribuição, vemos que nosso algoritmo de geração de dados foi satisfatório.

10. Simulação Monte Carlo

10.1. Introdução

Nesta seção, abordaremos o conceito de simulação Monte Carlo, ou método de integração Monte Carlo. Este método, em geral, pode ser resumido como a estimativa estatística do valor de uma integral usando avaliações de um integrando em um conjunto de pontos escolhidos aleatoriamente de uma distribuição com suporte sobre o intervalo de integração. Essa estimativa via simulação Monte Carlo pode ser útil em uma ampla variedade da teoria de probabilidades. Por exemplo, nas análises Bayesianas, a média a posteriori pode ser escrita na forma de uma integral, mas normalmente não pode ser avaliada analiticamente; o cálculo do risco na teoria da decisão Bayesiana depende da integração; o víes e o erro-quadrático médio de uma estimativa de um parâmetro de uma distribuição de probabilidade também pode depender do uso de integrais; dentre muitas outras aplicações. Neste sentido, nosso foco será ilustrar, no R, como pode ser realizado o processo de simulação Monte Carlo.

10.2. O Método de Monte Carlo

No geral, muitas quantidades de interesse em análises estatísticas inferenciais podem ser expressas como a esperança de uma função de uma variável aleatória, digamos \(E\{h(X)\}\). Por exemplo, seja \(f\) a densidade da variável aleatória X, e \(\mu\) a esperança (média) de \(h(X)\) em relação a \(f\). Quando umaamostra aleatória (i.i.d.) \(X_1, \ldots, X_n\) é obtida de \(f\), podemos aproximar \(\mu\) pela média amostral da seguinte forma:

\[\hat \mu_{MC} = \dfrac{1}{n}\sum_{i=1}^{n} h(X_i) \rightarrow \int h(x)f(x) dx = \mu\]

em que \(n\rightarrow \infty\), pela lei dos grandes números. Além disso, seja \(v(x) = [h(x) − \mu]^2\), e assuma que \(h(X)^2\) tem esperança finita sob \(f\). Então a variância amostral de \(\hat \mu_{MC}\) é \(\sigma^2/n = E\{v(X)/n\}\), onde a esperança é tomada em relação a \(f\). Uma abordagem de Monte Carlo semelhante pode ser usada para estimar \(\sigma^2\). Tal abordagem é descrita por:

\[ \hat{var}\{\hat\mu_{MC}\}= \dfrac{1}{n-1} \sum_{i=1}^{n}\left[h(X_i) - \hat\mu_{MC}\right]^2\] Quando \(\sigma^2\) existe, o teorema do limite central implica que \(\mu_{MC}\) tem uma distribuição aproximadamente normal para \(n\) grandes. Em geral, a integração de Monte Carlo fornece convergência lenta de orderm \(O(n^{−1/2})\).

10.3. Viés e EQM

Nosso objetivo aqui é trabalhar como o conceito anterior sobre o método de Monte Carlo para avaliar o viés (V) e o erro quadrático médio (EQM) dos estimadores de máxima verossimilhança de um dado modelo de probabilidade. Neste contexto, considere uma amostra aleatória (i.i.d.) \((X_1, \ldots, X_n)\) de uma variável aleatória X cuja a função de verossimilhança é definida por:

\[L(\theta) = \prod_{i=1}^{n} f_\theta (x_i)\]

em que \(f_\theta\) é a função densidade de probabilidade, e \(\theta\) é o vetor de parâmetros. O estimador de máxima verossimilhança é obtido a partir da maximização de \(L\) ou, equivalentemente, \(\ell(\theta) = \ln(L(\theta))\), e é denotado por \(\hat\theta\). A partir desse estimador, nosso interesse, é utilizar o método de Monte Carlo para obter o V e EQM de \(\theta\) de acordo com as equações:

\[V(\widehat{\theta}) = \frac{1}{N} \sum_{i = 1}^{N}(\widehat{\theta}_{i} - \theta)\] \[EQM(\widehat{\theta}) = \frac{1}{N} \sum_{i = 1}^{N}(\widehat{\theta}_{i} - \theta)^{2}\] em que \(\theta\) é o vetor de parâmetros, e \(N\) é o número de simulações para o método de Monte Carlo.

10.4. Experimentos Monte Carlo

Para exemplificar o método de Monte Carlo para o cálculo do V e do EQM, iremos, nesta seção, considerar o modelo de probabilidade Weibull em alguns cenários práticos diferentes:

  • Cenário 1: Variação apenas do número de simulações, com valores paramétricos e tamanhos amostrais fixos.
  • Cenário 2: Variação o número de simulações, e os tamanhos amostrais, com valores paramétricos fixos.
  • Cenário 3: Variação o número de simulações, os tamanhos amostrais, e os valores paramétricos.

10.4.1. Cenário 1

Para o nosso primeiro cenário, podemos realizar a simulação de duas formas: com o uso de laço de repetições, ou sem o uso de laço de repetições. Em ambos os casos, o resultado será o mesmo, mas a performance é diferente. Vamos começar com o primeiro caso, usando laço de repetição.

## Carregar os pacotes necessários

library(fitdistrplus)

## Criar uma matriz para armazenar os resultados

out <- matrix(ncol = 2, nrow = 1000)

## Estrutura da simulação

N   <- 1000 # Número de simulações Monte Carlo
n   <- 100 # Tamanho de amostra
par <- c(1.5, 2) # Parâmetros fixos

## Simulação

for(i in 1:N)
{
  set.seed(123) # Semente utilizada para reprodução de resultados
    dados       <- rweibull(n = n, shape = par[1], scale = par[2])
    aux           <- fitdist(data = dados, distr = "weibull", start = list(shape = par[1], scale = par[2]))$estimate
    out[i, ]    <- aux
}

## Viés 

V.1 <- mean(out[,1] - par[1], na.rm = TRUE) # Parâmetro 'shape'
V.1
## [1] 0.03000923
V.2 <- mean(out[,2] - par[2], na.rm = TRUE) # Parâmetro 'scale'
V.2
## [1] -0.001203025
## EQM

EQM.1 <- mean((out[,1] - par[1])^2, na.rm = TRUE) # Parâmetro 'shape'
EQM.1
## [1] 0.000900554
EQM.2 <- mean((out[,2] - par[2])^2, na.rm = TRUE) # Parâmetro 'scale'
EQM.2
## [1] 1.44727e-06

Para o segundo caso, sem uso de laço de repetições, temos:

## Carregar os pacotes necessários

library(fitdistrplus)

## Estrutura da simulação

N   <- 1000 # Número de simulações Monte Carlo
n   <- 100 # Tamanho de amostra
par <- c(1.5, 2) # Parâmetros fixos

## Simulação

set.seed(123) # Semente utilizada para reprodução de resultados
x                   <- rweibull(n * N, shape = par[1], scale = par[2]) # Gerar uma amostra grande de dados
dados           <- data.frame(matrix(x, ncol = N)) 
emvs              <- lapply(dados, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2])) # Estimativas
out         <- do.call("rbind", lapply(emvs, "[[", "estimate")) # Resgatar as estimativas

## Viés 

V.1 <- mean(out[,1] - par[1], na.rm = TRUE) # Parâmetro 'shape'
V.1
## [1] 0.02645651
V.2 <- mean(out[,2] - par[2], na.rm = TRUE) # Parâmetro 'scale'
V.2
## [1] 0.001423539
## EQM

EQM.1 <- mean((out[,1] - par[1])^2, na.rm = TRUE) # Parâmetro 'shape'
EQM.1
## [1] 0.0150484
EQM.2 <- mean((out[,2] - par[2])^2, na.rm = TRUE) # Parâmetro 'scale'
EQM.2
## [1] 0.01907509

10.4.1. Cenário 2

Para o nosso segundo cenário, diferentemente do primeiro cenário, não é possível “cortar” os laços de repetição, no entanto, podemos trabalhar com apenas um laço de repetição. Para melhor visualizar esse cenário, que agora trabalha com a variação do tamanho amostra, vamos considerar um experimento Monte Carlo com \(N = 10, 100, 1000\) simulações.

  • Primeiro caso: 10 simulações:
## Carregar os pacotes necessários

library(fitdistrplus)

## Estrutura da simulação

set.seed(123) # Semente utilizada para reprodução de resultados
N       <- 10 # Número de simulações Monte Carlo
n       <- seq(20, 200, 30) # Tamanhos amostrais
par     <- c(1.5, 2) # Parâmetros fixos
x               <- rweibull(max(n) * N, shape = par[1], scale = par[2]) # Gerar um conjunto 'gigante' de dados
dados   <- matrix(x, ncol = N) # Converter os dados gerados em uma matriz
entrada <- data.frame(dados) # Converter os dados gerados em um data.frame
est         <- matrix(nrow = 7, ncol = 2) # Matriz para salvar as estimativas
k           <- 1

## Simulação

for (amostra in n)
{
    X               <-      entrada[1:amostra, ]
    emvs            <-      lapply(X, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2]))
    est[k, ]      <-        apply(do.call("rbind", lapply(emvs, "[[", "estimate")), 2, mean)
    cat(paste('Tamanho de amostra:', amostra), "\n")
    k               <-      k + 1
}
## Tamanho de amostra: 20 
## Tamanho de amostra: 50 
## Tamanho de amostra: 80 
## Tamanho de amostra: 110 
## Tamanho de amostra: 140 
## Tamanho de amostra: 170 
## Tamanho de amostra: 200
## Viés 

V <- est - matrix(par, nrow = 7, ncol = 2, byrow = T) 
V[ ,1] # Parâmetro 'shape'
## [1]  0.066818632  0.042037932  0.014662212  0.006603664 -0.010478278
## [6]  0.010820619  0.020569848
V[ ,2] # Parâmetro 'scale'
## [1] -0.031637787 -0.038191886 -0.017591478 -0.028220655 -0.021895271
## [6]  0.007247391  0.009080085
# Gráficos
par(mfrow = c(1,2))
plot(n, V[ ,1], type = "b", ylab = 'Vies: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, V[ ,2], type = "b", ylab = 'Vies: scale', xlab = 'Tamanho de amostra', pch = 19) 
abline(h = 0, col = 'red', lwd = 2)

## EQM

EQM <- (est - matrix(par, nrow = 7, ncol = 2, byrow = T))^2
EQM[ ,1] # Parâmetro 'shape'
## [1] 4.464730e-03 1.767188e-03 2.149805e-04 4.360838e-05 1.097943e-04
## [6] 1.170858e-04 4.231186e-04
EQM[ ,2] # Parâmetro 'scale'
## [1] 1.000950e-03 1.458620e-03 3.094601e-04 7.964054e-04 4.794029e-04
## [6] 5.252467e-05 8.244795e-05
# Gráficos
par(mfrow = c(1,2))
plot(n, EQM[ ,1], type = "b", ylab = 'EQM: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, EQM[ ,2], type = "b", ylab = 'EQM: scale', xlab = 'Tamanho de amostra', pch = 19) 
abline(h = 0, col = 'red', lwd = 2)

  • Segundo caso: 100 simulações:
## Carregar os pacotes necessários

library(fitdistrplus)

## Estrutura da simulação

set.seed(123) # Semente utilizada para reprodução de resultados
N       <- 100 # Número de simulações Monte Carlo
n       <- seq(20, 200, 30) # Tamanhos amostrais
par     <- c(1.5, 2) # Parâmetros fixos
x               <- rweibull(max(n) * N, shape = par[1], scale = par[2]) # Gerar um conjunto 'gigante' de dados
dados   <- matrix(x, ncol = N) # Converter os dados gerados em uma matriz
entrada <- data.frame(dados) # Converter os dados gerados em um data.frame
est         <- matrix(nrow = 7, ncol = 2) # Matriz para salvar as estimativas
k           <- 1

## Simulação

for (amostra in n)
{
    X               <-      entrada[1:amostra, ]
    emvs            <-      lapply(X, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2]))
    est[k, ]      <-        apply(do.call("rbind", lapply(emvs, "[[", "estimate")), 2, mean)
    cat(paste('Tamanho de amostra:', amostra), "\n")
    k               <-      k + 1
}
## Tamanho de amostra: 20 
## Tamanho de amostra: 50 
## Tamanho de amostra: 80 
## Tamanho de amostra: 110 
## Tamanho de amostra: 140 
## Tamanho de amostra: 170 
## Tamanho de amostra: 200
## Viés 

V <- est - matrix(par, nrow = 7, ncol = 2, byrow = T) 
V[ ,1] # Parâmetro 'shape'
## [1] 0.11079699 0.06280482 0.04201301 0.02856296 0.02508897 0.02427846 0.02453957
V[ ,2] # Parâmetro 'scale'
## [1] -0.002206710 -0.005140790  0.015984249  0.005484858  0.007104302
## [6]  0.012600827  0.013083646
# Gráficos
par(mfrow = c(1,2))
plot(n, V[ ,1], type = "b", ylab = 'Vies: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, V[ ,2], type = "b", ylab = 'Vies: scale', xlab = 'Tamanho de amostra', pch = 19) 
abline(h = 0, col = 'red', lwd = 2)

## EQM

EQM <- (est - matrix(par, nrow = 7, ncol = 2, byrow = T))^2
EQM[ ,1] # Parâmetro 'shape'
## [1] 0.0122759722 0.0039444449 0.0017650928 0.0008158428 0.0006294566
## [6] 0.0005894437 0.0006021904
EQM[ ,2] # Parâmetro 'scale'
## [1] 4.869570e-06 2.642773e-05 2.554962e-04 3.008367e-05 5.047111e-05
## [6] 1.587808e-04 1.711818e-04
# Gráficos
par(mfrow = c(1,2))
plot(n, EQM[ ,1], type = "b", ylab = 'EQM: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, EQM[ ,2], type = "b", ylab = 'EQM: scale', xlab = 'Tamanho de amostra', pch = 19) 
abline(h = 0, col = 'red', lwd = 2)

  • Terceiro caso: 1000 simulações:
## Carregar os pacotes necessários

library(fitdistrplus)

## Estrutura da simulação

set.seed(123) # Semente utilizada para reprodução de resultados
N       <- 1000 # Número de simulações Monte Carlo
n       <- seq(20, 200, 30) # Tamanhos amostrais
par     <- c(1.5, 2) # Parâmetros fixos
x               <- rweibull(max(n) * N, shape = par[1], scale = par[2]) # Gerar um conjunto 'gigante' de dados
dados   <- matrix(x, ncol = N) # Converter os dados gerados em uma matriz
entrada <- data.frame(dados) # Converter os dados gerados em um data.frame
est         <- matrix(nrow = 7, ncol = 2) # Matriz para salvar as estimativas
k           <- 1

## Simulação

for (amostra in n)
{
    X               <-      entrada[1:amostra, ]
    emvs            <-      lapply(X, fitdist, distr = "weibull", start = list(shape = par[1], scale = par[2]))
    est[k, ]      <-        apply(do.call("rbind", lapply(emvs, "[[", "estimate")), 2, mean)
    cat(paste('Tamanho de amostra:', amostra), "\n")
    k               <-      k + 1
}
## Tamanho de amostra: 20 
## Tamanho de amostra: 50 
## Tamanho de amostra: 80 
## Tamanho de amostra: 110 
## Tamanho de amostra: 140 
## Tamanho de amostra: 170 
## Tamanho de amostra: 200
## Viés 

V <- est - matrix(par, nrow = 7, ncol = 2, byrow = T) 
V[ ,1] # Parâmetro 'shape'
## [1] 0.09862877 0.03294364 0.02467645 0.01725981 0.01528471 0.01355049 0.01170997
V[ ,2] # Parâmetro 'scale'
## [1] -0.013653828 -0.006704724  0.005292578  0.003561858  0.002797891
## [6]  0.001024363  0.001448506
# Gráficos
par(mfrow = c(1,2))
plot(n, V[ ,1], type = "b", ylab = 'Vies: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, V[ ,2], type = "b", ylab = 'Vies: scale', xlab = 'Tamanho de amostra', pch = 19) 
abline(h = 0, col = 'red', lwd = 2)

## EQM

EQM <- (est - matrix(par, nrow = 7, ncol = 2, byrow = T))^2
EQM[ ,1] # Parâmetro 'shape'
## [1] 0.0097276352 0.0010852835 0.0006089270 0.0002979010 0.0002336223
## [6] 0.0001836158 0.0001371235
EQM[ ,2] # Parâmetro 'scale'
## [1] 1.864270e-04 4.495332e-05 2.801138e-05 1.268683e-05 7.828193e-06
## [6] 1.049320e-06 2.098170e-06
# Gráficos
par(mfrow = c(1,2))
plot(n, EQM[ ,1], type = "b", ylab = 'EQM: shape', xlab = 'Tamanho de amostra', pch = 19)
abline(h = 0, col = 'red', lwd = 2)
plot(n, EQM[ ,2], type = "b", ylab = 'EQM: scale', xlab = 'Tamanho de amostra', pch = 19) 
abline(h = 0, col = 'red', lwd = 2)

10.4.3. Cenário 3

Para finalizar, consideremos agora o nosso terceiro e último cenário que trabalha com a variação do tamanho amostra, variação do numéro de simulações e, por fim, variação dos parâmetros. Este é o tipo de cenário é chamado de experimento Monte Carlo completo, e é fundamental para a avaliar do viés e performance de modelos probabilísticos, em geral. Assim, para melhor visualizar o funcionamento desse cenário, vamos considerar um experimento Monte Carlo com \(N = 10, 100, 1000\) simulações.

  • Primeiro caso: 10 simulações:
# Simulação Monte Carlo - Distribuição Weibull

require(fitdistrplus)

# Estimador de Máxima Verossimilhança

emv.weibull <- function(x, par)
{
    fit     <- try(fitdist(x, 'weibull', start = list(shape = par[[1]], scale = par[[2]]))$estimate, silent = TRUE)
    if(!is.numeric(fit)) fit <- NA
    return(fit)
}

# Estrutura da Simulação

set.seed(1212) # Definir a semente para resultados reprodutíveis

shapes <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de forma (shape)
scales <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de escala (scale)

param  <- expand.grid(shapes, scales) # Cenários

B      <- 10 # Número de simulações

nmax   <- 100 # Tamanho de amostra máximo
enes   <- seq(10, nmax, 10) # Tamanhos de amostras

v.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Viés do estimador do parâmetro de forma (shape)

v.scale <- v.shape # Viés do estimador do parâmetro de escala (scale)

e.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Erro-quadrático-médio do estimador do parâmetro de forma (shape)

e.scale <- e.shape # Erro-quadrático-médio do estimador do parâmetro de escala (scale)

# Laço de Repetição da Simulação

for(i in 1:nrow(param))
{
    k <- 1
    X <- rweibull(nmax * B, shape = param[i,1], scale = param[i,2])
    X <- matrix(X, ncol = B, nrow = nmax)
    
    for(n in enes)
    {
        x   <- data.frame(X[1:n,])
        fit <- sapply(x, emv.weibull, par = param[i,])
        
        v.shape[k,i] <- mean(fit[][1,] - param[i,1], na.rm = TRUE) # Viés: Shape
        v.scale[k,i] <- mean(fit[][2,] - param[i,2], na.rm = TRUE) # Viés: Scale
        
        e.shape[k,i] <- mean((fit[][1,] - param[i,1])^2, na.rm = TRUE) # EQM: Shape
        e.scale[k,i] <- mean((fit[][2,] - param[i,2])^2, na.rm = TRUE) # EQM: Scale
        
        k <- k + 1
        
        cat(i, paste('Tamanho de amostra:', n), paste('Shape:', param[i,1], 'Scale:', param[i,2]), "\n") # Visualiza os Cenários
    }
}
## 1 Tamanho de amostra: 10 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 20 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 30 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 40 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 50 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 60 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 70 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 80 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 90 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 100 Shape: 0.5 Scale: 0.5 
## 2 Tamanho de amostra: 10 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 20 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 30 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 40 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 50 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 60 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 70 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 80 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 90 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 100 Shape: 1 Scale: 0.5 
## 3 Tamanho de amostra: 10 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 20 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 30 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 40 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 50 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 60 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 70 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 80 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 90 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 100 Shape: 1.5 Scale: 0.5 
## 4 Tamanho de amostra: 10 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 20 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 30 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 40 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 50 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 60 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 70 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 80 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 90 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 100 Shape: 0.5 Scale: 1 
## 5 Tamanho de amostra: 10 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 20 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 30 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 40 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 50 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 60 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 70 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 80 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 90 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 100 Shape: 1 Scale: 1 
## 6 Tamanho de amostra: 10 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 20 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 30 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 40 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 50 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 60 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 70 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 80 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 90 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 100 Shape: 1.5 Scale: 1 
## 7 Tamanho de amostra: 10 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 20 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 30 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 40 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 50 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 60 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 70 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 80 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 90 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 100 Shape: 0.5 Scale: 1.5 
## 8 Tamanho de amostra: 10 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 20 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 30 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 40 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 50 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 60 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 70 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 80 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 90 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 100 Shape: 1 Scale: 1.5 
## 9 Tamanho de amostra: 10 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 20 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 30 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 40 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 50 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 60 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 70 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 80 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 90 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 100 Shape: 1.5 Scale: 1.5
# Gráficos

par(mfrow = c(1,2))
matplot(v.shape, type = 'b', ylab = 'Vies: shape', xlab = 'Tamanho de amostra')
matplot(v.scale, type = 'b', ylab = 'Vies: scale', xlab = 'Tamanho de amostra')

par(mfrow = c(1,2))
matplot(e.shape, type = 'b', ylab = 'EQM: shape', xlab = 'Tamanho de amostra')
matplot(e.scale, type = 'b', ylab = 'EQM: scale', xlab = 'Tamanho de amostra')

  • Segundo caso: 100 simulações:
# Simulação Monte Carlo - Distribuição Weibull

require(fitdistrplus)

# Estimador de Máxima Verossimilhança

emv.weibull <- function(x, par)
{
    fit     <- try(fitdist(x, 'weibull', start = list(shape = par[[1]], scale = par[[2]]))$estimate, silent = TRUE)
    if(!is.numeric(fit)) fit <- NA
    return(fit)
}

# Estrutura da Simulação

set.seed(1212) # Definir a semente para resultados reprodutíveis

shapes <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de forma (shape)
scales <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de escala (scale)

param  <- expand.grid(shapes, scales) # Cenários

B      <- 100 # Número de simulações

nmax   <- 100 # Tamanho de amostra máximo
enes   <- seq(10, nmax, 10) # Tamanhos de amostras

v.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Viés do estimador do parâmetro de forma (shape)

v.scale <- v.shape # Viés do estimador do parâmetro de escala (scale)

e.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Erro-quadrático-médio do estimador do parâmetro de forma (shape)

e.scale <- e.shape # Erro-quadrático-médio do estimador do parâmetro de escala (scale)

# Laço de Repetição da Simulação

for(i in 1:nrow(param))
{
    k <- 1
    X <- rweibull(nmax * B, shape = param[i,1], scale = param[i,2])
    X <- matrix(X, ncol = B, nrow = nmax)
    
    for(n in enes)
    {
        x   <- data.frame(X[1:n,])
        fit <- sapply(x, emv.weibull, par = param[i,])
        
        v.shape[k,i] <- mean(fit[][1,] - param[i,1], na.rm = TRUE) # Viés: Shape
        v.scale[k,i] <- mean(fit[][2,] - param[i,2], na.rm = TRUE) # Viés: Scale
        
        e.shape[k,i] <- mean((fit[][1,] - param[i,1])^2, na.rm = TRUE) # EQM: Shape
        e.scale[k,i] <- mean((fit[][2,] - param[i,2])^2, na.rm = TRUE) # EQM: Scale
        
        k <- k + 1
        
        cat(i, paste('Tamanho de amostra:', n), paste('Shape:', param[i,1], 'Scale:', param[i,2]), "\n") # Visualiza os Cenários
    }
}
## 1 Tamanho de amostra: 10 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 20 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 30 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 40 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 50 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 60 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 70 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 80 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 90 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 100 Shape: 0.5 Scale: 0.5 
## 2 Tamanho de amostra: 10 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 20 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 30 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 40 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 50 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 60 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 70 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 80 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 90 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 100 Shape: 1 Scale: 0.5 
## 3 Tamanho de amostra: 10 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 20 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 30 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 40 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 50 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 60 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 70 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 80 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 90 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 100 Shape: 1.5 Scale: 0.5 
## 4 Tamanho de amostra: 10 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 20 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 30 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 40 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 50 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 60 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 70 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 80 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 90 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 100 Shape: 0.5 Scale: 1 
## 5 Tamanho de amostra: 10 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 20 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 30 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 40 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 50 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 60 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 70 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 80 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 90 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 100 Shape: 1 Scale: 1 
## 6 Tamanho de amostra: 10 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 20 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 30 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 40 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 50 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 60 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 70 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 80 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 90 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 100 Shape: 1.5 Scale: 1 
## 7 Tamanho de amostra: 10 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 20 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 30 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 40 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 50 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 60 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 70 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 80 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 90 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 100 Shape: 0.5 Scale: 1.5 
## 8 Tamanho de amostra: 10 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 20 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 30 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 40 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 50 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 60 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 70 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 80 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 90 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 100 Shape: 1 Scale: 1.5 
## 9 Tamanho de amostra: 10 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 20 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 30 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 40 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 50 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 60 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 70 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 80 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 90 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 100 Shape: 1.5 Scale: 1.5
# Gráficos

par(mfrow = c(1,2))
matplot(v.shape, type = 'b', ylab = 'Vies: shape', xlab = 'Tamanho de amostra')
matplot(v.scale, type = 'b', ylab = 'Vies: scale', xlab = 'Tamanho de amostra')

par(mfrow = c(1,2))
matplot(e.shape, type = 'b', ylab = 'EQM: shape', xlab = 'Tamanho de amostra')
matplot(e.scale, type = 'b', ylab = 'EQM: scale', xlab = 'Tamanho de amostra')

  • Terceiro caso: 1000 simulações:
# Simulação Monte Carlo - Distribuição Weibull

require(fitdistrplus)

# Estimador de Máxima Verossimilhança

emv.weibull <- function(x, par)
{
    fit     <- try(fitdist(x, 'weibull', start = list(shape = par[[1]], scale = par[[2]]))$estimate, silent = TRUE)
    if(!is.numeric(fit)) fit <- NA
    return(fit)
}

# Estrutura da Simulação

set.seed(1212) # Definir a semente para resultados reprodutíveis

shapes <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de forma (shape)
scales <- seq(0.5, 1.5, 0.5) # Definir os valores paramétricos para o parâmetro de escala (scale)

param  <- expand.grid(shapes, scales) # Cenários

B      <- 1000 # Número de simulações

nmax   <- 100 # Tamanho de amostra máximo
enes   <- seq(10, nmax, 10) # Tamanhos de amostras

v.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Viés do estimador do parâmetro de forma (shape)

v.scale <- v.shape # Viés do estimador do parâmetro de escala (scale)

e.shape <- matrix(nrow = length(enes), ncol = nrow(param)) # Erro-quadrático-médio do estimador do parâmetro de forma (shape)

e.scale <- e.shape # Erro-quadrático-médio do estimador do parâmetro de escala (scale)

# Laço de Repetição da Simulação

for(i in 1:nrow(param))
{
    k <- 1
    X <- rweibull(nmax * B, shape = param[i,1], scale = param[i,2])
    X <- matrix(X, ncol = B, nrow = nmax)
    
    for(n in enes)
    {
        x   <- data.frame(X[1:n,])
        fit <- sapply(x, emv.weibull, par = param[i,])
        
        v.shape[k,i] <- mean(fit[][1,] - param[i,1], na.rm = TRUE) # Viés: Shape
        v.scale[k,i] <- mean(fit[][2,] - param[i,2], na.rm = TRUE) # Viés: Scale
        
        e.shape[k,i] <- mean((fit[][1,] - param[i,1])^2, na.rm = TRUE) # EQM: Shape
        e.scale[k,i] <- mean((fit[][2,] - param[i,2])^2, na.rm = TRUE) # EQM: Scale
        
        k <- k + 1
        
        cat(i, paste('Tamanho de amostra:', n), paste('Shape:', param[i,1], 'Scale:', param[i,2]), "\n") # Visualiza os Cenários
    }
}
## 1 Tamanho de amostra: 10 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 20 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 30 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 40 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 50 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 60 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 70 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 80 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 90 Shape: 0.5 Scale: 0.5 
## 1 Tamanho de amostra: 100 Shape: 0.5 Scale: 0.5 
## 2 Tamanho de amostra: 10 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 20 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 30 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 40 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 50 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 60 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 70 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 80 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 90 Shape: 1 Scale: 0.5 
## 2 Tamanho de amostra: 100 Shape: 1 Scale: 0.5 
## 3 Tamanho de amostra: 10 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 20 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 30 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 40 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 50 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 60 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 70 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 80 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 90 Shape: 1.5 Scale: 0.5 
## 3 Tamanho de amostra: 100 Shape: 1.5 Scale: 0.5 
## 4 Tamanho de amostra: 10 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 20 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 30 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 40 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 50 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 60 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 70 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 80 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 90 Shape: 0.5 Scale: 1 
## 4 Tamanho de amostra: 100 Shape: 0.5 Scale: 1 
## 5 Tamanho de amostra: 10 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 20 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 30 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 40 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 50 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 60 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 70 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 80 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 90 Shape: 1 Scale: 1 
## 5 Tamanho de amostra: 100 Shape: 1 Scale: 1 
## 6 Tamanho de amostra: 10 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 20 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 30 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 40 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 50 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 60 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 70 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 80 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 90 Shape: 1.5 Scale: 1 
## 6 Tamanho de amostra: 100 Shape: 1.5 Scale: 1 
## 7 Tamanho de amostra: 10 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 20 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 30 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 40 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 50 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 60 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 70 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 80 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 90 Shape: 0.5 Scale: 1.5 
## 7 Tamanho de amostra: 100 Shape: 0.5 Scale: 1.5 
## 8 Tamanho de amostra: 10 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 20 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 30 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 40 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 50 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 60 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 70 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 80 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 90 Shape: 1 Scale: 1.5 
## 8 Tamanho de amostra: 100 Shape: 1 Scale: 1.5 
## 9 Tamanho de amostra: 10 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 20 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 30 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 40 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 50 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 60 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 70 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 80 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 90 Shape: 1.5 Scale: 1.5 
## 9 Tamanho de amostra: 100 Shape: 1.5 Scale: 1.5
# Gráficos

par(mfrow = c(1,2))
matplot(v.shape, type = 'b', ylab = 'Vies: shape', xlab = 'Tamanho de amostra')
matplot(v.scale, type = 'b', ylab = 'Vies: scale', xlab = 'Tamanho de amostra')

par(mfrow = c(1,2))
matplot(e.shape, type = 'b', ylab = 'EQM: shape', xlab = 'Tamanho de amostra')
matplot(e.scale, type = 'b', ylab = 'EQM: scale', xlab = 'Tamanho de amostra')

Referências Bibliográficas

  • MARTINEZ, E. Z. Bioestatística Para os Cursos de Graduação da Área da Saúde. Editora Blucher, 2015.
  • PAGANO, M., GAUVREAU, K. Princípios de Bioestatística. 2ª Edição. São Paulo: Cengage Learning, 2011.
  • DANIEL, W. W., CROSS, C. L. Biostatistics: A Foundation for Analysis in the Health Sciences. Wiley, 2018.
  • ROSNER, B. Fundamentos de Bioestatística. Cengage Learning Brasil, 2018.
  • CALLEGARI-JACQUES, S. M. Bioestatística: Princípios e Aplicações. Grupo A, 2003.
  • MAGALHÃES, M. N., DE LIMA, A. C. P. Noções de Probabilidade e Estatística. Editora da Universidade de São Paulo, 2002.
  • GIVENS, G. H.; HOETING, J. A. Computational statistics. John Wiley & Sons, 2012.
  • FREUND, J. E. Estatística Aplicada: Economia, Administração e Contabilidade. Bookman Editora, 2009.
  • DOANE, D. P.; SEWARD, L. E. Estatística Aplicada à Administração e Economia. Grupo A, 2014.
  • SEWARD, L. E., DOANE, D. P. Estatística Aplicada à Administração e Economia. AMGH editora, 2014.
  • SWEENEY, D J., WILLIAMS, T. A., ANDERSON, D. R. Estatística Aplicada à Administração e Economia. 5ª Edição. São Paulo: Cengage Learning, 2020.
  • LEVIN, J., FOX, J. A., FORDE, D. R. Estatística Para Ciências Humanas. São Paulo: Pearson, 2012.
  • VARIAN, H. R. Big Data: New Tricks for Econometrics. The Journal of Economic Perspectives 28 (2). American Economic Association: 3–27. 2014.
  • PEREIRA, T. N. Cointegração: Uma relação de equilíbrio de longo prazo. 2013.
  • BOWLES, M. Ensemble Packages in R. See: http://blog.revolutionanalytics.com/2014/04/Ensemble-Packages-in-R.html. 2015.